抽象数据类型
ADT的特性:表示泄露,抽象函数AF,表示不变量RI
- 抽象以及用户定义的类型
抽象数据类型是软件工程中一个通用原则的实例,它有很多名字: - 抽象:用更简单、更高层次的思想忽略或隐藏底层细节。(规约是一种客户端只需要理解千前置和后置条件来使用的抽象,不需要了解内部实现)
- 模块性:将一个系统划分为各个组件或模块,可以设计、实现、测试、推理和重用与系统的其他部分分开。(单元测试和规约使得方法变为模块)
- 封装:围绕模块建造防护,这样模块就负责它自己的内部行为和bug在系统的其他部分不能破坏其完整性。
- 信息隐藏:隐藏模块实现的详细信息系统的其余部分,以便以后可以更改这些细节而不改变系统的其他部分。(规约实用信息隐藏来给与实现部分实现方法的自由)
- 功能分离:让每个功能只由一个模块负责而不是将其分散到多个模块中。()
ADT是由操作定义的,与其内部如何实现无关。
- 分类操作和类型
操作器通常为构造函数或静态函数(工厂方法)
变值器通常返回void,也可能返回非空类型比如boolean
-
设计一套好的规约
(1) 设计简洁、一致的操作
(2) 要足以支持client对数据所做的所有操作需要,且用操作满足client需要的难度要低
(3) 要么抽象、要么具体,不要混合 — 要么针对抽象设计,要么针对具体应用的设计 -
表示独立性
表示独立性:client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端。
除非ADT的操作指明了具体的pre-和post-condition,否则不能改变ADT的内部表示——spec规定了client和implementer之间的契约。 -
测试ADT
测试creators, producers, and mutators:调用observers来观察这些operations的结果是否满足spec;
测试observers:调用creators, producers, and mutators等方法产生或改变对象,来看结果是否正确。 -
不变量Invariants
好的ADT在任何时候都要保持不变量。
由ADT来负责其不变量,与client端的任何行为无关。
保持不变量的原因:保持程序的正确性,容易发现错误。
表示泄露的危害:不仅影响不变性,也影响了表示独立性:无法在不影响客户端的情况下改变其内部表示。
除非迫不得已,否则不要把希望寄托于客户端上,ADT有责任保证自己的invariants,并避免表示泄露。
防止表示泄露:最好的办法就是使用immutable的类型,彻底避免表示泄露。其次是防御性拷贝等。 -
表示不变量和抽象函数(RI和AF)
表示空间:内部真实实现的实体。
抽象空间:客户端看到和使用的值。
ADT开发者关注表示空间R,client关注抽象空间。
表示空间到抽象空间的映射关系如下:
满射、未必单射、未必双射。
抽象函数AF即描述表示空间和抽象空间关系的函数。
表示不变性可以看做:
- 某个具体的“表示”是否是“合法的”;
- 所有表示值的一个子集,包含了所有合法的表示值;
- 一个条件,描述了什么是“合法”的表示值。
选择某种特定的表示方式R,进而指定某个子集是“合法”的(RI),并为该子集中的每个值做出“解释”(AF)——即如何映射到抽象空间中的值。
即使是同样的R、同样的RI,也可能有不同的AF,即“解释不同”。
设计ADT:
(1) 选择R和A;
(2) RI — 合法的表示值;
(3) 如何解释合法的表示值 —映射AF,做出具体的解释:每个rep value如何映射到abstract value。
checkRep:
在所有可能改变rep的方法内都要检查;
Observer方法可以不用,但建议也要检查。
程序员之间的“潜规则”:数据都“非空”;
- 有益的可变性
在表示空间与抽象空间的映射关系不变的前提下,ADT内部实现是可以改变rep的值,这种变化对客户端是不可见的,这种变化成为有益的可变性。
对immutable的ADT来说,它在A空间的abstract value应是不变的。
但其内部表示的R空间中的取值则可以是变化的。
- 规约撰写
ADT的规约里只能使用client可见的内容来撰写,包括参数、返回值、异常等。
如果规约里需要提及“值”,只能使用抽象空间中的“值”。
ADT的规约里也不应谈及任何内部表示的细节,以及表示空间中的任何值。
ADT的内部表示(私有属性)对外部都应严格不可见。
故在代码中以注释的形式写出AF和RI而不能在Javadoc文档中,防止被外部看到而破坏表示独立性/信息隐藏。
检查ADT保持不变量的标准:
- 由creators和producers构造;
- 由mutators和observers保持;
- 没有表示泄露的发生。
- 由ADT不变量来替换前置条件
用ADT不变量取代复杂的Precondition,相当于将复杂的precondition封装到了ADT内部。