1. ADT及其四种操作
抽象类型:强调"作用于数据上的操作",程序员和client无需关心数据如何具体存储,只需设计/使用操作即可。
ADT由操作定义,与其内部实现无关。
可变数据类型:提供了可改变其内部数据值的操作;
不可变数据类型:其操作不改变内部值,而构造新的对象。(没有mutators)
ADT操作分类:
1.
Creators 构造器:
不利用该类型对象产生一个新的对象
可能实现为构造函数或静态函数(factory
method)
2.
Producers 生产器:
用已有该类型对象产生新对象
如string.concat()(连接两个字符串,产生一个新的字符串)
3.
Observers 观察器
如list.size()返回int(不同于原类型)
4.
Mutators 变值器(改变对象属性的方法)
通常范围void,如果返回void,则必然意味着它改变了某些对象的内部状态
也可能范围非空类型(如容器类的put、add方法)
良好ADT的设计:靠“经验 法则”,提供一组操作,设计其行为规约
spec
Rules of thumb 1 设计简洁、一致的操作
设计一组简单操作,通过简单操作的组合实现复杂的操作。操作的行为应该 是内聚的。
Rules of thumb 2 要足以支持client对数据所做的所有操作需要,且 用操作满足client需要的难度要低
1.
ADT需要始终保持其不变量
Representation
Independence 表示独立性
表示独立性:client使用ADT时无需考虑其内部如何实现,ADT内部表现的变化不应该影响外部spec和客户端。
1.
Representation exposure 表示泄漏
如client能直接接触类成员变量。
表示泄漏影响表示不变量,也影响表示独立性:无法在不影响客户端的情况下改变其内部表示。
避免方法:private、final、defensive copy
1.
Invariants 不变量 & Representation
Invariant 表示不变量
ADT应保持其不变量在任何时候总是true;
ADT负责其不变量,与client的任何行为无关。
作用:保持程序的"正确性",容易发现错误。
1. Abstraction
Function 抽象函数
表示空间R
抽象空间A
值的实际实现本质
抽象表示(client看到和使用的值)
ADT实现者关注表示空间R
用户关注抽象空间A
R到A的映射
一定是满射:A中元素总有R中具体的实现
未必是单射:A中一个元素在R中可能有多重实现方式
未必是双射:R中表示不符合A中需求(如图中"abbc")
抽象函数AF:R和A之间映射关系的函数
AF:R->A
对于RI :R-> Boolean
表示不变性RI:某个具体的“表示”是否是“合法的” ▪ 也可将RI看作:所有表示值的一个子集,包含了所有合法的表示值 ▪ 也可将RI看作:一个条件,描述了什么是“合法”的表示值
同样的表示空间R,可以有不同的RI
即使是同样的R、同样的RI,也 可能有不同的AF,即“解释不同”
Documenting AF 、RI、Safety
from Rep Exposure
选择某种特定的表示方式R
进而指定某个子集是"合法"的(RI)
并为该子集中的每个值做出"解释"(AF)
即 如何映射
Safety from Rep Exposure
证明代码并未对外泄露其内部表示
保证不变量为true,不变量:
1.
通过creators和producers创建
2.
受mutators和observers保护
3.
无表示泄漏发生
ADT的规约里只能使用client可见的内容来撰写,包括参数、返 回值、异常等。
如果规约里 需要提及“值”,只能使用A空间 中的“值”。
ADT的规约里也不应谈及任何内部表示 的细节,以及R空间中的任何值
ADT的内部表示(私有属性)对外部都应严格不可见
故在代码中以注释的形式写出AF和RI而不 能在Javadoc文档中,防止被外部看到而破坏表示独立性/信息隐藏
变值器和观察器在执 行时必须保持不变性。
构造器和生产器在创建对象时要确保不变量为true
在每个 方法return之前,用checkRep()检查不变量是否得以保持。