抽象数据类型是软件工程中一个普遍原则的实例。
抽象:通过规约使得使用者只需弄懂规约并遵守前置条件,而不是让他们去弄懂底层的代码实现。
模块化:将系统分为一个个模块,每个模块可以单独的进行设计、实现、测试、推到,并且在剩下的开发中进行复用。
封装:在模块的外部建立起一道“围墙”,使它只对自己内部的行为负责,并且系统别处的bug不会影响到它内部的正确性。
信息隐藏:将模块的实现细节隐藏,使未来更改模块内部时不必改变外部代码。
功能分离:一个模块仅仅负责一个特性/功能,而不是将一个特性运用在很多模块上或一个模块拥有很多特性。
抽象类型的操作符大致分类:
构造器creator:创建一个该类型的新对象。一个创建者可能会接受一个对象作为参数,但是这个对象的类型不能是它创建对象对应的类型。
生产器producer:通过接受同类型的对象创建新的对象。例如String类里的concat方法就是一个生产者,它接受两个字符串然后据此产生一个新的字符串。
观察器observer:接受一个同类型的对象然后返回一个不同类型的对象/值。例如List的size方法,它返回一个int。
变值器mutator:改变对象的内容,例如List的add方法,它会在列表中添加一个元素。
抽象类型是通过它的操作定义的
一个好的抽象数据类型应该是表示独立的。这意味着它的使用和它的内部表示(实际的数据结构和实现)无关,所以内部表示的改变将对外度的代码没有影响。如果一个操作完全在规格说明中定义了前置条件和后置条件,使用者就应该知道他依赖什么,而你也可以安全的对内部实现进行更改。
要理解抽象数据类型ADT的实现,还需要理解四个思想。
- 不变量(invariants)
- 表示暴露(representation exposure)
- 抽象函数(abstraction functions)
- 表示不变量(representation invariants)
抽象函数是表示值到其对应的抽象值得映射:
AF:R->A
这种映射是满射,但不一定是单射。
表示不变量rep invariants是表示值到布尔值的映射:
RI:R->boolean
对于表示值R,当且仅当R被AF映射到了A,RI(R)为真。
一个ADT的实现不仅是选择表示域(规约)和抽象域(具体实现),同时也要决定哪一些表示值是合法的(表示不变量),合法表示会被怎么解释/映射(抽象函数)。
上图即实现者与客户在ADT中所了解与隔绝的。
最后总结如何写一个不变类型的ADT类。
- 不能有任何变值器
- 确保没有方法可能被重写
- 使每个成员变量都由final和private修饰
- 确保不会由表示暴露
- 实现对toString,hashCode,clone,equals等方法的重写。