ADT 的意义:抽象数据类型与表示独立性:如何设计良好的抽象数据结构,通过封 装来避免客户端获取数据的内部表示(即“表示泄露”),避免潜在 的bug——在client和implementer之间建立“防火墙”
ADT的特性:表示泄漏、抽象函数AF、表示不变量RI
基于数学的形式对ADT的这些核心特征进行描述并应用于设计中。
本章的主要目标:将数据和操作复合起来,构成ADT,学习 ADT的核心特征,以及如何设计“好的”ADT。
1 Abstraction and User-Defined Types 抽象和用户定义的数据类型。
除了编程语言所提供的基本数据类型和对象数据类型,程序员可定义 自己的数据类型。
数据抽象:由一组操作所刻画的数据类型。
传统数据类型与抽象数据类型的差别: 传统的类型定义:关注数据 的具体表示。抽象类型:强调“作用于数据上的操作”,程序员和 client无需关心数据如何具体存储的,只需设计/使用操作即可。
如图为bool上的操作。An abstract data type Bool has the following operations。
ADT是由操作定义的,与其内部如何实现无关!
2 分类类型和操作 Classifying Types and Operations
1)可变和不可变数据类型 Mutable and immutable types
可变类型的对象:提供了可改变其 内部数据的值的操作。
不变数据类型: 其操作不改变 内部值,而是构造新的对象。
2)对抽象类型的操作进行分类 Classifying the operations of an abstract type.
Creators create new objects of the type. 构造器
Producers create new objects from old objects of the type. 生产器
Observers take objects of the abstract type and return objects of a different type. 观察器
Mutators change objects. 变值器,改变对象属性的方法.
构造器:可能实现为构造函数或静 态函数,A creator implemented as a static method is often called a factory method 工厂方法
变值器通常返 回void,如果返回值为void,则必然意 味着它改变了对象的某些内部状态.
3 抽象数据类型示例 Abstract Data Type Example.
;
关于构造器 变值器 产生器 观察器的例子如下
4 设计一个抽象数据 Designing an Abstract Type
. 设计好的ADT,靠“经 验法则”,提供一组操作,设计其行为规约 spec
原则:设计简洁、一致的操作
要足以支持client对数据所做的所有操作需要,且 用操作满足client需要的难度要低
要么抽象、要么具体,不要混合 --- 要么针对抽象 设计,要么针对具体应用的设计
5.表示独立性:Representation Independence
表示独立性:client使用ADT时无需考虑其内部如何实 现,ADT内部表示的变化不应影响外部spec和客户端。
除非ADT的操作指明了具体的pre和post-condition,否则不能改变ADT的内部表示——spec规定了 client和implementer之间的契约。
§ 因为此数据类型是不可变的,所以子字符串操作实际上不必将字符复 制到新的数组中。
它可以只指向原始MyString对象的字符数组,并 跟踪新子字符串对象表示的开始和结束。
一个违反RI的例子
6 测试一个ADT Testing an Abstract Data Type
这些测试不可避免地 相互影响
测试creators, producers, and mutators:调用observers来观察这些 operations的结果是否满足spec;
测试observers:调用creators, producers, and mutators等方法产生或 改变对象,来看结果是否正确。
风险:如果被依赖的其他方法有错误,可能导致被测试方法的测试结 果失效。
7 Invariants不变量
Invariants of an ADT保持不变量
不变量:在任何时候总是true。由ADT来 负责其不变量,与client端的任何行为无关。
为什么需要不变量:保持程序的“正 确性”,容易发现错误。
. 确 信String不会改变,则不需考虑会有人改变他。
如果没有这个不变性, 那么在所有使用String的地方,都要检查其是否改变了。
总是要假设client有“恶意”破坏 ADT的不变量---defensive programming(有意或无意)
表示泄露:不仅影响不变性,也影响了表示独立性:无法在不影响客户 端的情况下改变其内部表示。
除非迫不得已,否则不要把希望寄托于客户端上,ADT有责任保证自 己的invariants,并避免“表示泄露”。最好的办法就是 使用immutable的类型,彻底避免表示泄漏。
8 表示不变量和抽象函数 Rep Invariant and Abstraction Function
一般情况下ADT的表示比较简单,有些时候需要复杂表示。
A:抽象值构成的空间:client看到和使用的值
R:表示空间
ADT开 发者关注表示空间R,client关注抽象空间A
抽象函数:R和A之间映射关系的函数,即如 何去解释R中的每一个值为A中的每一个值
R中的部分值并非合法的, 在A中无映射值。
表示不变性RI:某个具体的“表示”是否是“合法的”
也可将RI看作:所有表示值的一个子集,包含了所有合法的表示值
也可将RI看作:一个条件,描述了什么是“合法”的表示值
选择某种特定的表示方式R,进而指定某个子集是“合 法”的(RI),并为该子集中的每个值做出“解释”(AF)——即如何映射 到抽象空间中的值。
设计ADT:(1) 选择R和A;(2) RI --- 合法的表示值; (3) 如何解释合法的表示值 ---映射AF 做出具体的解释:每个rep value如何映射到abstract value
对immutable的ADT来说,它在A空间的abstract value应是不变的。 但其内部表示的R空间中的取值则可以是变化的。