目录
2.2行为等价性(Behavioral equivalence)
3.2*表示独立性(Representation Independence)
3.4*Rep Invariant and Abstraction Function(RI and AF)
3.5有益的可变性(Beneficent mutation)
1.数据类型和类型检验
1.1静态/动态类型检查
(Java是静态类型检查,在编译阶段进行检查,Java不进行动态类型检测)
静态类型检查:语法、类名/函数名、参数数目、参数类型、返回值类型
动态类型检查:非法的参数值、非法的返回值、越界、空指针
注意List<String>和List<Object>是在静态类型检测中报错。
1.2Mutable/Immutable
Java可进行自动垃圾回收。 Immutable好处:安全,但浪费空间。
*final特性: final 限定的是引用不变(如果mutable改变值不会报错),final类无法派生子类,final方法无法被子类重写。
使用Mutable可获得更好的性能,也适合多个模块间共享数据,但不够安全!
Date也是mutable类!避免使用!
可以使用java.time包中的其他immutable类型的类:LocalDateTime, Instant等
immutable拷贝时间 O(n2)
传参数尽量用immutable类型(保证参数不变性),如果传mutable参数可先进行defensive copying
必须通过类中的方法来改变类中的属性(防止信息泄露)
1.3*Snapshot diagram(重点)
Immutable对象:用双线椭圆
不可变的引用(用final修饰的变量):用双线箭头
1.4Array and Collections
Iterator
mutable类型,有两种方法:next()和hasNext(),next()方法是mutate的
需要注意,当用Iterator迭代List中元素,涉及到remove时,由于remove后List内元素索引会发生改变,会出现错误。
Collections
基本类型及其封装对象类型都是immutable的
List、Map、ArrayList等都是mutable的
可以利用Collections类提供的方法将mutable类包装成immutable
Collections.unmodifiableList Collections.unmodifiableSet Collections.unmodifiableMap
这种包装器得到的结果是不可变的,只能看,不能修改(其实就是disabled了一些mutate方法或者让其抛出异常)
这种”不可变“是在运行阶段获得的,编译阶段无法对此进行静态检查
虽然不能用包装后的对象对其进行修改,但依旧能用包装前的对象进行修改
2.设计规约
2.1规约(Specifications)
规约不要给出任何方法的实现。
规约不能被程序进行检测 (×) ——函数描述可以被检测(参数类型),注释不能被检测。
规约注释包含:功能描述、输入数据限制、返回值
2.2行为等价性(Behavioral equivalence)
一般站在用户(客户端)的角度看(可能会给一定前提),可根据规约判断是否行为等价
2.3前置条件和后置条件
前置条件,关键词requires 后置条件,关键词effects
前置条件是对客户端的约束,在使用方法时必须满足的条件;后置条件是对开发者的约束,方法结束时必须满足的条件
如果前置条件满足,后置条件必须满足;如果前置条件不满足,后置条件想淦神魔都可以
2.4Java中的规约
Java中的静态类型声明是一种规约,可据此进行静态类型检查static checking
方法前的注释也是一种规约,但需要人工判定其是否满足
前置条件在 @param中,后置条件在 @return和@throws中(@return中不能包含具体类型,如 @return boolean)
如果方法对输入的参数做了改变,一定要在规约中说明
2.5*规约的强弱(重点)
判定标准
规约强度 S2 >= S1 : 前置条件更弱,后置条件更强
spec变强,即更放松的前置条件 + 更严格的后置条件
Diagramming specifications
每一个点代表一个方法的实现。如果某个具体实现满足规约,就落在其范围内;否则在其之外。
更强的规约,表示为更小的区域。(实现的自由度小,面积小)
3. 抽象数据类型(ADT)
ADT 的特性:表示泄漏、抽象函数 AF 、表示不变量 RI
基于数学的形式对 ADT 的这些核心特征进行描述并应用于设计中
ADT是由操作定义的,与其内部实现无关
3.1ADT四种操作类型
构造器(Creator):创建一个该类型的新对象(可能实现为构造函数或静态函数)
生产器(Producer):从一个类型的旧对象创建一个新对象(如String中concat方法)
观察器(Observer):返回一个不同类型的对象(如List中的size方法)
变值器(Mutator):改变对象属性的方法(如List中的add方法)
如果一个构造器是用静态方法来实现的,通常称为工厂方法 (factory method):如Java中String类
的String.valueOf(Object obj)方法
Mutators通常返回void,但也可以返回非空类型,如Set.add()返回的类型为boolean
immutable类型的ADT无变值器(Mutator)
判断是哪种操作类型,首先需要确定是mutable还是immutable
注:Collections.unmodifiableList() 是 producer; 如果一个方法既改变了对象属性,也返回了不同类型的对象,它是变值器Mutator
3.2*表示独立性(Representation Independence)
表示独立性:client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应该影响外部spec和客户端。
上述实例就违反了表示独立性,因为public限定了这是一个instance variable,但是后面的final又限定了客户端不能够实际改变这个类的immutability属性
3.3*不变性(Invariants)
由ADT来负责其不变量,与client端的任何行为无关
表示泄露出现情况:
public 类型的数据 -> private final
mutable 类型共享引用
不应该包含mutate方法
当复制代价很高时,可在规约中强加条件(但是不推荐!)
3.4*Rep Invariant and Abstraction Function(RI and AF)
R : 表示空间 A:抽象空间(ADT开发者关注表示空间R,client关注抽象空间A)
R -> A 的映射:1. 满射:所有抽象值都要有一个rep value 2. 未必单射:一个抽象值可能有多个表示 3. 未必双射:不是所有的表示值都有对应的抽象值
抽象函数(AF):R和A之间映射关系的函数
表示不变性 RI:某个具体的“表示”是否是“合法的”(R -> boolean)
在ADT的规约里若出现"值",也只能是A空间的"值"
3.5有益的可变性(Beneficent mutation)
即immutable的属性是可变的,但是要保证用户角度是一样的
例如:[1, 2] 和 [2, 4]在A空间可均表示1/2