抽象数据类型
对类型和操作的分类
可变类型的对象:提供了可改变其内部数据的值的操作
不变数据类型: 其操作不改变内部值,而是构造新的对象
即可变类型的对象在修改的时候是修改其内部的值,而不可变数据类型是新创建一个类型,然后引用新创建的类型。
对抽象类型的操作进行分类
构造器:创建该类型的新对象。
生产器:从该类型的旧对象创建新对象。
观察器:获取抽象类型的对象并返回不同类型的对象。
变值器:改变对象属性的方法。
以下是一些常见类的方法的类型:
方法 | 类型 |
---|---|
Integer.valueOf() | Creator |
BigInteger.mod() | Producer |
List.addAll() | Mutator |
String.toUpperCase() | Producer |
Set.contains() | Observer |
Map.keySet() | Observer |
Collections.unmodifiableList() | Producer |
BufferedReader.readLine() | Mutator |
表示独立性(Representation Independence)
表示独立性:client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端。除非ADT的操作指明了具体的pre- 和post-condition,否则不能改变ADT的内部表示——spec规定了client和implementer之间的契约。
即当ADT的内部实现改变的时候(例如之前使用List实现,现在改为用Set实现),该ADT的方法需要能够同时兼容这两种实现,而不能只是这个方法兼容List而不兼容Set,这种就违背了表示独立性的原则。
上图就是因为是使用了只兼容于List类的方法,这种方法用于Set是不存在的,违反了“无论属性和内部实现如何改变,对于某个操作用户都感受不到”这个原则。
表示暴露(representation exposure)
不变性的第一个威胁来自客户端可以直接访问其字段。
而表示暴露就是一个类直接将它的属性暴露给客户,这样客户可以直接对其属性进行操作,这些操作可能会导致一些非法的属性修改。所以需要避免表示暴露。
避免表示暴露的方法
将由public修饰的属性改为private final修饰,这样可以保证这些对象的属性在构造后不会被重新分配。
但对于那些需要返回该类属性值的观察器,也不能直接返回该这个属性,而是要进行一个copy,返回那个copy,这样可以避免表示暴露。
但最好的办法就是使用immutable的类型,彻底避免表示泄露。
抽象函数(AF)和表示不变性(RI)
抽象函数的定义
对于编写该类的程序员来说呈现在ta面前的是一个表示空间,其中表示了该类型的具体实现方式。而对于客户来说呈现给ta的就是一个抽象空间。抽象函数(Abstract Function)就是一个从表示空间到抽象空间的映射。即如何去解释表示空间中的每一个值为抽象空间中的每一个值。
每个抽象函数都是满射的,但是并不一定是单射和双射的。(因为表示空间中的部分值并非合法的,在抽象空间中无映射值)
表示不变性
表示不变性RI:某个具体的“表示”是否是“合法的”。
也可将RI看作:所有表示值的一个子集,包含了所有合法的表示值。
也可将RI看作:一个条件,描述了什么是“合法”的表示值。