CLASS 06 · 2026-02-23 · 抽象
抽象数据类型(ADT)是「便于修改」这一品质的核心工具。本讲学习类型由其操作定义的思想,以及表示独立性如何让使用者不受内部实现变化的影响。
抽象相关的几个核心概念:
数据抽象的核心思想:一个类型由你能对它执行的操作来刻画。
使用者不需要知道一个类型「内部是什么」,只需要知道「它能做什么」。这就是抽象的力量。
抽象类型的操作可以分为四类:
concat。size()、isEmpty()。add()、remove()。类型 \(T\) 的操作集合及其规格,完全刻画了「\(T\) 是什么」。两个类型如果提供相同的操作且规格相同,则从使用者角度看它们就是相同的类型。
// 一个不可变整数集合的 ADT
class ImmutableSet<T> {
// 创建者
constructor(elements: T[]) { /* ... */ }
// 生产者
add(elem: T): ImmutableSet<T> { /* 返回新集合 */ }
remove(elem: T): ImmutableSet<T> { /* 返回新集合 */ }
// 观察者
contains(elem: T): boolean { /* ... */ }
size(): number { /* ... */ }
isEmpty(): boolean { /* ... */ }
}
好的 ADT 应遵循以下原则:
表示独立性(Representation independence):抽象类型的使用独立于其内部表示。
这意味着我们可以把一个 ImmutableSet 的内部实现从数组换成链表、从哈希表换成平衡树,而使用者代码完全不需要修改——只要操作的行为(规格)保持不变。
// 表示方式一:用数组存储
class ImmutableSetArr<T> {
private readonly elements: T[];
// ...
}
// 表示方式二:用链表存储
class ImmutableSetList<T> {
private readonly head: ListNode<T> | null;
// ...
}
// 两种实现满足同一 ADT 规格,使用者代码无需修改
TypeScript 提供了多种实现 ADT 的方式:类、接口 + 类、枚举等。接口特别适合表达「操作集合」这一抽象,而类负责具体实现。
为 ADT 构建测试套件的方法:为它的每个操作编写测试。测试应验证每个操作是否满足其规格,并验证操作之间的交互是否正确。
操作定义类型ADT 由其操作集合及规格完全刻画。
四类操作创建者、生产者、观察者、修改者。
表示独立内部表示可自由更换,使用者代码不受影响。