文章目录
通过封装避免表示泄露。
1. ADT
1.1 Abstraction and User-Defined Types
抽象类型:强调“作用于数据上的操作”,程序员和用户无需关心数据如何具体存储,只需要设计/使用操作。
Bool类型的操作example
An abstract data type Bool has the following operations:
–true: Bool
–false: Bool
–and: Bool ×Bool → Bool
–or: Bool ×Bool → Bool
–not: Bool → Bool
但是有许多可能的方式去实现Bool,可以是bit(0/1),String(true/false),int(8/5 or >=0/<0)。
一个抽象数据类型是通过它的操作定义的,与内部如何实现无关。
1.2 Classifying Types and Operations
Mutable:提供了可改变其内部数据的值的操作。
Immutable:其操作不改变内部值,而是构造新的对象。
有些类型会提供两种形式,一种可变,一种不可变。StringBuilder(mutable) String(immutable)
Creators 构造器:create new objects of the type.
Producers 生产器:create new objects from old objects of the type.
Observers 观察器:take objects of the abstract type and return objects of a different type. ps:个人理解,用于观察对象的某些特征,需要其他类型来表示。
Mutators 变值器:change objects. immutable类型无变值器。
1.2.1 signature of a creator
可能实现为构造函数或静态函数(工厂方法),String.valueof(Object obj)就是工厂方法的构造器。
1.2.2 signature of a mutator
变值器通常返回viod,如果返回值为void,则必然意味着它改变了对象的某些内部状态。也有其他返回值,比如boolean,或者对象本身。
1.3 Designing an Abstract Type
设计好的ADT,靠“经验法则”,提供一组操作,设计其行为规约的spec,不需要有具体的程序,根据程序的功能设计规约。
example:add方法并没有方法体
rule1:设计简洁、一致的操作。 rule2:要足以支持client对数据所做的所有操作需要,且用操作满足client需要的难度更低。
1.4 Testing ADT
测试creator,producters,and mutators:调用observers来观察这些操作的结果是否满足spec。
测试observers:调用creator,producters,and mutators等方法产生或改变对象,来看结果是否正确。
**风险:**如果被依赖的其他方法有错误,可能导致被测试方法的测试结果失效。
保证测试用例能够覆盖所有的部分。
2. representation independence 表示独立性
表示独立性:client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端。
除非ADT的操作指明了具体的pre-和post-condition,否则不能改变ADT的内部表示。
违反(violate)RI的例子:能够通过f直接访问属性,不可以!!!
保持RI的例子:
3. Invariants 不变量
final表示指向的地址不变,如果变量是mutable类型的,还是可能改变。
如果复制代价很高时,不得不将希望寄托于客户端,在规约中写明不可改变的值。否则,ADT有责任保证自己的不变量,并避免表示泄露。
summary:
-
不要将可变参数合并到对象中;使用防御型复制。
-
返回可变属性的防御性复制,或者返回可变属性的unmodifiable view 不可变视图。
-
使用不可变组件,消除防御性复制的需要。
如何建立不变量:
对象初始化和发生变化时,不变量都要为true。在return之前,要用checkRep()检查不变量是否保持。
三个标准来检查是否保持不变量:
- established by creators and producers;
- preserved by mutators, and observers;
- no representation exposure occurs
4. Rep Invariant and Abstraction Function
R(表示空间)和A(抽象空间)之间是满射、未必单射、未必双射。规约中的值只能使用A空间中的值,不能谈及任何内部表示细节,以及R空间中的值。所以AF和RI是注释形式,而不是java doc。
AF 抽象函数:R和A之间映射关系的函数,即如何去解释R中的每一个值为A中的每一个值。
RI 表示不变性:某个具体的“表示”是否是“合法的”。
例如:AF和RI都是对属性的描述与判断。
选择某种特定的表示方式R,进而指定某个子集是“合法”的(RI),并为该子集中的每个值做出“解释”(AF)——即如何映射
要精准记录RI(rep中所有的fields何为有效),和AF(如何解释每一个R值)。
设计ADT:选择R和A;RI–合法的表示值;AF–如何解释合法的表示值。
checkRep();用于监测RI是否满足。在所有可能改变rep的方法内(creators, producers, and mutators)都要检查。observer方法可以不用,但是建议也要检查。
5. Beneficent mutation 有益可变性 必考!
对immutable的ADT来说,它在A空间的abstract value应是不变的,但其内部表示的R空间中的取值则可以是变化的。这种变化叫做有益可变性。
observer方法mutate rep。这种mutator只是改变了R值,并未改变A值,对于client来说是immutable的---->AF并不是单射。
但是!并不代表immutable的类中可以随意出现mutator。
6. rep exposure 自证清白
mutable类型数据要制作防御性拷贝。
7. ADT invariants replace preconditions ADT取代规约的前置条件
用ADT不变量取代复杂的 Precondition,相当于将复杂的precondition封装到了ADT内部。
example:
如果出错可以在编译阶段体现。通过名字SortedSet可以传达意思。更容易去改变,更改SortedSet后不需要改变exclusiveOr或者其他客户端。