软件构造这门课是比较具有挑战性,知识点多且琐碎的,因此,我将结合ppt和回放,对软件构造的知识点来进行整理,主要用于对于知识点的记忆和复习,如果能够对你有所帮助,那荣幸之至。
在这篇博客中,我将对第三章 3.3 这节的知识点来进行整理总结。
3.3 抽象数据型ADT
抽象数据型的本质是加了一定约束条件的类。用户在调用抽象数据型的时候,只能通过类提供的方法去使用这个类(类中所有的属性都是private类型)表示独立性指的是用户使用类的操作而不关心类中具体是怎么实现的。
3.3.1 抽象和用户定义类型
用户定义类型指的是用户自己写的类。抽象指的是用户只能看到操作而不知道具体的实现。ADT是由操作定义的,与其内部如何实现无关,用户只能通过操作去使用类。
3.3.2 类中操作的分类
- creators 构造器
创建类对象,从无到有的操作 - producers 生产器
给定一个旧的对象,返回同一类型的新的对象
比如说给定两个string,返回两个string相连形成的新string操作。 - observers 观察器
给定当前类型的对象,返回一个其他类型的值
比如说给定一个string,返回该string的长度 - mutators 变值器
改变属性的值的方法
注:如果一个ADT提供了能够改变其内部数据的值的操作,则称这个ADT是mutable的,若其操作都不改变内部的值,而是构造新的对象的话(即不存在mutator操作)则称这个ADT是immutable的。
矛盾:用户想构造类必须用到构造器,然而构造器含有类的实现方法,不应该让用户看到。
解决方法:引入静态的creator,也称为工厂方法。(第四章具体讲)
注2:如果一个方法的返回值为空,那么这个方法一定是mutator方法。
3.3.3 设计抽象数据型
首先将用户看到的操作列出来,设计行为规约spec
- 操作应当是简单,一致的
- 操作要足够支持类的使用
3.3.4 表示独立性
表示独立性(representation independence):用户在使用ADT的时候不需要考虑其内部属性及实现,ADT内部变化也不应该影响外部spec和客户端。
一个类中应该包含三个部分:
- specification
指规约等用户能够看到的东西 - representation
表示,也就是类的属性 - implementation
类的具体实现
3.3.5 抽象数据型的测试
对ADT进行测试的时候,会有方法之间的调用
- 测试creator,producer,mutator的时候,需要调用observer来观察这些操作的结果是否满足spec
- 测试observer的时候需要调用creator,producer,mutator来产生或者改变对象,来看结果是否正确。
这样做会引起的风险就是测试时会出现方法之间的依赖。
3.3.6 不变性
指程序运行的过程中,保持一些量不改变。例如说对于一个时间区间,开始时间必须要早于结束时间。在程序执行的任何时候,不变性都不能够被打破。保持不变量由ADT全权负责,尽量不要寄希望于用户上。
ADT最重要的不变性,也就是必须时刻保持的就是表示独立和避免表示暴露
3.3.7 表示不变性RI和抽象函数AF
程序设计的过程中,存在着两个空间
- 抽象空间A:用户所关注的真实的数据
- 表示空间R:开发者所关注的计算机中所存储的数据
注:所有的A空间中的数据,都能够在R空间中找到一个或多个对应,R空间中的数据可能在A空间中没有对应。
抽象函数:R和A之间的映射关系,即如何去解释R中的值为A中的某个值
表示不变性:是R空间的一个子集,表明某个表示是否合法,说白了就是一个判定条件。
设计AF和RI首先取决于内部的表示
- 选择R和A(数据结构)
- 设计RI(选择合法表示值)
- 设计AF(如何解释合法表示值)
注:AF,RI一定需要写到注释中,保证在写代码的任何时间都去遵守。但是AF,RI不应该被用户看到。
需要随时检查RI是否满足——checkRep,在所有可能改变rep的方法内部都需要调用checkRep。observer方法以防万一,最好也用。
3.3.8 其他
有益的变化:指R空间中的数据改变,但是对应的A空间中的数据不变。
这表明immutable类的属性也并不是一定不会改变的,只要从用户的角度看没有改变就行。
在代码中必须要以注释的形式说明AF,RI,safety from exposure
表示暴露的安全声明用来自证清白。如果规约中需要提到值,那么只能够使用A空间中的值。
如果检测不变量需要耗费较多资源,则这项工作可以通过precondition推给用户来做。