抽象数据类型

表示泄露

AF

RI

Abstraction and User-Defined Types 抽象和用户定义的类型

抽象类型:强调“作用于数据上的操作”,程序员和client无需关心数据如何具体存储的,只需设计/使用操作即可。

ADT是由操作定义的,与其内部如何实现无关!

2 Classifying Types and Operations 分类类型和操作

可变类型的对象:提供了可改变其内部数据的值的操作。不变数据类型: 其操作不改变内部值,而是构造新的对象

  • Creator 构造器 可以将对象作为参数,但是不能把对象类型作为构造对象。(t* -> T)
  • Producer 生产器 例如 String 的 contact 方法,将两个两个字符串连接为一个新的字符串。(T+, t* -> T)
  • Observer 观察器 参看数据,例如 size() 方法。(T+, t* -> t)
  • Mutator 变值器 改变对象属性的方法,例如 List 的 add() 方法。(T+, t* -> void | t | T)

其中T代表抽象类型,t代表其他类型。

creator 构造器

A creator is either implemented as a constructor , like new ArrayList(), or simply a static method instead, like Arrays.asList(), List.of(). 构造器:可能实现为构造函数或静态函数。

factory method 工厂方法:a creator implemented as a static method.

mutator 变值器

变值器通常返回 void,变值器也可能返回非空类型。

3 Abstract Data Type Examples

只有 mutable 数据类型才有 mutator(这改变具体的对象)

数据类型 Operations
int(immutable) int
String(immutable) String
List(mutable) List

4 Designing an Abstract Type 设计抽象类型

设计好的ADT,靠“经验法则”,提供一组操作,设计其行为规约 spec.

  1. Rules of thumb 1 设计简洁、一致的操作
  2. Rules of thumb 2 要足以支持client对数据所做的所有操作需要,且用操作满足client需要的难度要低

5 Representation Independence 表示独立性

表示独立性:client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端。

抽象类型的使用与其表示(用于实现它的实际数据结构或数据字段)无关,因此表示的更改不会影响抽象类型本身之外的代码。

除非ADT的操作指明了具体的pre-和post-condition,否则不能改变ADT的内部表示——spec规定了client和implementer之间的契约。

6 Testing an Abstract Data Type

  • 测试creators, producers, and mutators:调用observers来观察这些operations的结果是否满足spec;
  • 测试observers:调用creators, producers, and mutators等方法产生或改变对象,来看结果是否正确。

7 Invariants 不变性

不变量 Invariant:在任何时候总是true,immutability 就是一个典型的“不变量”(一旦被创建始终表示相同的值),不变量由ADT来负责。

Invariants:(1) private(限制只能在类中访问的 fields 和方法); (2) immutable type; (3) 对于 mutable 外部引用可以直接改变属性的值,所以考虑 copy; (4) final(保证在构造对象后不会重新分配此不可变类型的字段)。当代价很高时,将不变性交给用户(写到规约),但由此引发的bug会很多。

representation exposure 表示泄露:类以外的代码也能直接地修改表示。不仅影响不变性,也影响了表示独立性:无法在不影响客户端的情况下改变其内部表示。

除非迫不得已,否则不要把希望寄托于客户端上,ADT有责任保证自己的invariants,并避免“表示泄露”。最好的办法就是使用 immutable 的类型,彻底避免表示泄露(例如用 java.time.ZonedDateTime 而不是 mutable java.util.Date)

  • Don’t incorporate mutable parameters into object; make defensive copies
  • Return defensive copies of mutable fields…
  • Or return unmodifiable view of mutable fields
  • Real lesson – use immutable components, to eliminate the need for defensive copying

8 Rep Invariant and Abstraction Function (RI AF)