优秀的规约是程序编写和执行的基础,能够有效提升效果和质量。
抽象数据类型(ADT),强调“作用于数据上的操作”,程序员和客户端不关心数据如何存储,只需设计和使用操作即可。这意味着,ADT是由操作定义的,与其内部如何实现无关。优秀的ADT能够分离程序中数据结构的形式和对其使用的方式,并通过封装来避免数据泄露。
一。Spec
规约(Specification),意即程序与客户端达成一定约定,用于区分职责范围,明确说明程序的要求与功能,客户端不需要了解函数程序的实现过程,只需要理解规约并使用。
1. 规约构成
前置条件:对客户端的约束,意指在使用方法时必须满足的条件,包括方法签名中的参数数量和类型,以及附加的参数类型,参数交互关系等。
后置条件:对开发者的约束,意指在方法结束后必须满足的条件,包括返回类型、声明的异常检查,以及附加的返回值与输入值的关系、抛出异常的时间和类型,对象是否发生及怎样发生变化等。
意外行为:当前置条件不满足时要执行的行为,包括发出警告、抛出异常等。
2.规约强弱判断
一个规约的强度取决于它的前置条件和后置条件。前置条件越宽松、后置条件越严格,规约的强度就越高。即一个强大的规约能使客户端轻松的使用,并由程序严格的执行。
二。类型与操作
1.可变与不可变类型
可变类型的对象提供了可以改变其内部数据的值的操作,而不可变数据类型不存在相应操作,只能构造新的对象。
对于不可变类型对象,建议使用Iterator迭代器来实现它的遍历查找和删除操作。值得注意的是该迭代器也不能修改对象内容,且只能单向遍历。
2.各类操作
Creators构造器:用于产生对象,一般由构造函数或静态函数实现。
Producers生产器:由旧的对象生成新的对象
Observers观察器:取出ADT的某些特征
Mutators变值器:改变对象属性。
对于后两种操作,常见于类的getter和setter中,值得注意的是,在获取类的属性时,考虑到防御性编程,可以生成一个新的对象来存储该属性并返回,而不是之间返回对应属性。
三。ADT设计
1.设计规则
设计要保持简洁一致,通过设计简单的操作来组合实现复杂的功能。
要支持客户端对数据的所有操作需要,且尽量以最简单的方式满足。
确定类中所有对象的可见性,使客户端需要访问的保持Public,其它的保持private。
2.表示独立性
表示独立性(Representation Independence),客户端使用ADT时无需考虑程序内部如何实现,而ADT内部的变化也不影响外部的客户端以及相应的规约。仅通过前置条件和后置条件来刻画ADT的操作,由Spec作为客户端和接口之间的关系,让客户端明确可以使用的内容,让接口明确可以更改的内容。
3.不变量
不变量(Invariants)时ADT中始终需要保持和注意的,这部分不会设计客户端的任何操作。
可以通过private,final等关键字保护其私有不变性,并通过防御性拷贝,使返回客户端的数据与自身隔离。
4.抽象函数和表示不变性
表示域:space of representation values,即包含的是值的具体的实现实体
抽象域:space of abstract values,即包含的是类型设计时支持使用的值,这些值是由表示域抽象出来的。
抽象函数(Abstraction Function)就是反应表示域和抽象域之间映射关系的函数。
表示不变性(Rep Invarient):规定某个值的合法要求,也可看做所有表示值的子集,其中包含了所有的合法表示值。
5.表示泄露的安全声明
为了防止可变类中变量及其地址遭到泄露,需要采用防御式编程进行保护。其中就包括使用private、final关键字确认类型,尽量使用不可变数据类型(immutable)等。
四.ADT中的Spec
ADT规约只能使用客户端可见的内容来进行编写,包括参数、返回值、异常等
规约中涉及的“值”只能表示抽象域中的值
ADT的规约中不能包含内部表示的细节以及表示域中的值
ADT内部私有属性对外应严格不可见
上述即为我对软件构造过程和配置管理的一点理解,希望能在巩固自身掌握的同时为其他阅读者带来一点收获。💪