软件构造学习心得——第二部分ADT+OOP(4-8讲)(上)
内容
基本数据类型、对象数据类型
静态类型检查、动态类型检查
Mutable/Immutable
值的改变、引用的改变
防御式拷贝
Snapshot diagram
Specification、前置/后置条件
行为等价性
规约的强度
ADT操作的四种类型
表示独立性
表示泄露
不变量、表示不变量RI
表示空间、抽象空间、AF
以注释的形式撰写AF、RI
接口、抽象类、具体类
继承、override
多态、overload
泛型
等价性equals()和==
equals()的自反、传递、对称
hashCode()
不可变对象的引用等价性、对象等价性
可变对象的观察等价性、行为等价性
基本数据类型、对象数据类型
基本数据类型:int、long、boolean、char、double(小写开头)
对象数据类型:String、BigInteger(大写开头)
对象数据类型有继承关系
静态类型检查、动态类型检查
前者在编译阶段进行类型检查,后者在运行阶段进行类型检查
必然的,静态类型检查 >>(好于) 动态 >>(好于) 无检查
静态类型检查:可在编译阶段发现错误,避免了将错误带入到运行阶段,可提高程序正确性/健壮性
静态:关于“类型”的检查,不考虑值
动态:关于“值”的检查
除0错误不是动态!
Mutable/Immutable&&值的改变、引用的改变&&防御式拷贝&&Snapshot diagram
赋值:数据改变/引用改变
尽量使用final变量(引用不变)作为方法的输入参数、作为局部变量
final类无法派生子类
final变量无法改变值/引用
final方法无法被子类重写
不变对象:一旦被创建,始终指向同一个值/引用
可变对象:拥有方法可以修改自己的值/引用
eg:
String 是immutable的
StringBuilder是mutable的
当只有一个引用指向该值,没有区别;有多个引用的时候,差异就出现了
可变的优势:
性能更好、共享
不可变优势:
更“安全”,在其他质量指标上表现更好
如何改进:
1.防御式拷贝(new…)
2.局部变量,不会涉及共享;只有一个引用
3.包装器
snapshot:
eg:
Complex data types: Arrays and Collections(list、set、map)都是mutable的,迭代器也是mutable的
这里subjects是ArrayList
注意不能用subjects.remove
包装器:将mutable转为immutable
Specification、前置/后置条件、强度
规约:只讲“能做什么”,不讲“怎么实现”
提高代码效率、解耦
异常:前置条件不满足
前置条件不满足,则方法可做任何事情。
– “你违约在先,我自然不遵守承诺”
尽量避免使用mutable的对象作为参数,且尽量不改变输入参数
规约的强度:
spec变强:更放松的前置条件+更严格的后置条件
强的规约可以替代弱的规约!
可以比不出强弱!
图:
在规约里使用抽象类型,可以给方法的实现体与客户端更大的自由度;
不写Precondition,就要在代码内部check;若代价太大,在规约里加入precondition, 把责任交给client
惯用做法是:不限定太强的precondition,而是在postcondition中抛出异常:输入不合法
ADT操作的四种类型
可变类型的对象:提供了可改变其内部数据的值的操作
不变数据类型: 其操作不改变内部值,而是构造新的对象
四种类型:(考试时给一个方法问是什么类型)
构造器:可能实现为构造函数或静态函数(工厂方法)
变值器:通常返回void,如果返回值为void,则必然意味着它改变了对象的某些内部状态,当然也可以返回其他的,比如boolean,或者自己
immutable, has no mutators.
表示独立性&&表示泄露&&不变量、表示不变量RI&&表示空间、抽象空间、AF&&以注释的形式撰写AF、RI
表示独立性:client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端。
除非ADT的操作指明了具体的pre- 和post-condition,否则不能改变ADT的内部表示——spec规定了client和implementer之间的契约。
不变量:在任何时候总是true
eg:immutability就是一个典型的“不变量”
表示泄露:外部代码可以直接改变内部表示
除非迫不得已,否则不要把希望寄托于客户端上,ADT有责任保证自己的invariants,并避免“表示泄露”。最好的办法就是使用immutable的类型,彻底避免表示泄露
RI和AF:
AF : R → A,映射,抽象函数,必满射,未必单射
RI : R → boolean,表示空间值的合法性(要会写RI!)
表示不变性RI:某个具体的“表示”是否是“合法的”
也可将RI看作:所有表示值的一个子集,包含了所有合法的表示值
也可将RI看作:一个条件,描述了什么是“合法”的表示值
不同的内部表示,需要设计不同的AF和RI
选择某种特定的表示方式R,进而指定某个子集是“合法”的(RI),并为该子集中的每个值做出“解释”(AF)——即如何映射到抽象空间中的值。
即使是同样的R、同样的RI,也可能有不同的AF,即“解释不同”。
**checkRep()**在所有可能改变rep的方法内都要检查
Observer方法可以不用,但 建议也要检查,以防止你的“万一”
几种方式:
防御拷贝
包装器
返回immutable类
检查保持不变量的三种方式:
本博客完成于6/20,于7/3修改
下篇链接