目录
一、抽象数据类型和用户自定义类型
1.用户自定义类型
数据抽象:由一组操作刻画的数据类型
传统类型:关注数据的具体表示
抽象类型:强调作用于数据上的操作,不关注数据如何存储
2.抽象类型被它的操作定义
二、多种类型和操作
可变和不可变数据类型:
可变类型的对象:提供了可改变其内部数据的值的操作
不变数据类型:其操作不改变内部值,而是构造新的对象
抽象数据类型的多种操作:
①构造器:赋初值,可能实现为构造函数或静态函数
②生产器:产生新的对象
③观察器:返回ADT的某些参数值
④变值器:改变对象属性,通常返回void
三、设计ADT
①设计简洁、一致的操作
②要足以支持client对数据所作的所有操作需要,且用操作满足client需要的难度要低
③要么抽象要么具体,不要混合
四、表示独立性
表示独立性:客户端使用ADT时无需考虑其内部实现,ADT内部表示的变化不应影响外部spec和客户端
除非ADT的操作指明了具体的pre-和post-condition,否则不能改变ADT的内部表示
五、测试ADT
测试构造器、生产器、变值器,调用观察器来观察操作结果是否满足spec
测试观察器,调用其余三者等方法产生或改变对象,来看结果是否正确
如果被依赖的其他方法有错误,可能导致被测试方法的测试结果失败
六、表示不变量
不变量:在任何时候都是true
由ADT来负责不变量,于客户端的任何行为无关
保持不变量可以保持程序的正确性,容易发现错误
表示泄露:不仅影响不变性,也影响了表示独立性,无法在不影响客户端的情况下改变其内部表示
可以使用private、final、immutable类型避免表示泄露
七、表示不变量和抽象函数
表示空间R:一般情况下ADT的表示比较简单,有时候需要复杂表示
抽象空间A:抽象值构成的空间,客户端看到和使用的值
抽象函数:是从R到A的满射,如何解释R中每个值为A中的每个值
表示不变性RI:某个具体的表示是否是合法的
所有表示值的一个子集,包含了所有合法的表示值
一个条件,描述了什么是合法的表示值
A集合无法单独定义RI和AF,对于一个A,可能有多种表示形式,不同的内部表示需要设计不同的AF和RI
设计ADT:选中R→指定RI→做出解释(AF)
(即使是同样的R,RI,也可能有不同的AF)
八、有益的变化
对immutable的ADT来说,它在A空间的抽象值应是不变的
但其内部表示的R空间中的取值是可以变化的
这种改变只是改变了R值,并未改变A值,对客户端来说是不可变的
九、记录AF和RI
在代码中用注释的形式记录AF和RI
精确记录RI:rep中的所有fields何为有效
精确记录AF:如何解释每一个R值
表示泄露的安全声明:给出理由,证明代码并未对外泄露其内部表示
ADT的规约里只能使用client可见的内容来撰写,包括参数、返回值、异常等
如果规约里需要提及值,只能使用A空间中的值
ADT的规约里也不能谈及任何内部表示的细节,以及R空间中的任何值
ADT的内部表示对外部都应严格不可见
在代码中以注释形式写出AF和RI,而不能写在Javadoc文档中,防止被外部看到而破坏表示独立性/信息隐藏
构造器和生产器在创建对象时要确保不变量为true,变值器和观察器在执行时必须保持不变性,在每个方法return时要用checkrep()检查不变性是否得以保持
十、用ADT不变量取代precondition
用ADT不变量取代复杂的precondition,相当于将复杂的precondition封装到了ADT内部