抽象数据类型(ADT)理论
1. 抽象和用户定义类型
编程语言通常提供内置类型和内置过程。用户可以定义自己的数据类型和过程,这些称为用户定义类型。数据抽象是一个重要概念,它强调通过一组操作来刻画数据类型,使程序员和客户端无需关心数据的具体存储方式。
2. 数据类型和操作的分类
2.1 可变和不可变数据类型
- 可变数据类型:提供可改变内部值的操作
- 不可变数据类型:操作不可改变其内部值
2.2 四种基本操作
- 构造器:创建新对象
- 生产器:从旧对象创建新对象
- 观察器:获取对象信息并返回不同类型的对象
- 变值器:改变对象属性
2.3 方法签名
不同类型的操作通常有不同的方法签名特征。例如,构造器可能是构造函数或静态函数,变值器的返回值类型可能暗示了其行为。
3. 设计抽象数据类型
良好的ADT设计应遵循以下原则:
- 设计简洁、一致的操作
- 提供足够强大的功能以满足用户需求
- 针对抽象操作或具体应用进行设计,避免混合
设计时应关注规约(spec),包括参数、返回值、异常等,但不应涉及内部表示细节。
4. 表示独立性
表示独立性是ADT的重要特性,意味着客户端使用ADT时无需考虑其内部实现。这通过充分的规约来实现,明确了客户端可以依赖的内容和实现者可以安全更改的内容。
5. 测试抽象数据类型
测试ADT时应根据操作类型采用不同策略:
- 测试创建、生产和变值操作:通过观察器验证结果
- 测试观察器:通过其他操作产生或改变对象,验证观察结果
需注意方法间的依赖关系可能影响测试结果。
6. 不变量
ADT需要维护其不变量,这是程序在任何状态下都保持为真的属性。不变量有助于保持程序正确性和发现错误。设计时应假设客户端可能试图破坏不变量,因此需要采取防御措施。
7. 表示泄漏
表示泄漏指内部数据结构被外部客户访问或修改。这不仅影响不变量,也影响表示独立性。防止表示泄漏的方法包括使用私有字段、final关键字,以及在规约中明确说明。最佳实践是使用不可变类型。
8. 表示不变量(RI)和抽象函数(AF)
8.1 值的两个空间
- 表示空间(R):开发者使用的值
- 抽象空间(A):用户使用的值
抽象函数(AF)定义了R和A之间的关系。表示不变量(RI)定义了合法的表示值。
8.2 AF和RI的多样性
同一个ADT可以有多种表示,不同的内部表示需要不同的AF和RI。即使是相同的表示空间和RI,也可能有不同的AF。
9. 有益的可变性
对于不可变的ADT,只要保证表示值继续映射到相同的抽象值,就可以自由修改表示值。这种变化称为有益的可变性,对客户端是不可见的。
10. 记录AF、RI和表示泄漏的安全性
良好的实践包括在类中记录AF和RI,并明确说明防止表示泄漏的措施。这包括检查参数和返回值是否可能导致表示泄漏。
11. ADT不变量替换前提条件
使用ADT不变量替换复杂的前提条件可以提高安全性、可理解性和可变性。这种方法将复杂的条件封装在ADT内部,简化了客户端代码。