@[TOC]抽象数据类型
本文基于徐汉川老师的2020年软件构造课程撰写
能够分离程序中数据结构的形式和对其使用的方式,如何设计良好的抽象数据结构,通过封装来避免客户端获取数据的内部表示(即“表示泄露”),避免潜在的bug—— 在client 和implementer 之间建立“防火墙”。
而ADT也具有一定的特性:ADT 的特性:不变量、表示泄漏、抽象函数AF 、表示不变量RI
除了编程语言所提供的基本数据类型和对象数据类型,程序员可定义
自己的数据类型。
以数据抽象为例:数据抽象:由一组操作所刻画的数据类型。
– A number is something you can add and multiply;
数字就是可以用于加法和乘法操作的对象。
– A string is something you can concatenate and take substrings of;
字符串就是可以用于连接和截取的对象。
程序员在定义自己的数据类型的时候只关心设计、操作:抽象类型:强调“作用于数据上的操作”,程序员和client无需关心数据如何具体存储的,只需设计/使用操作即可。
Bool的设计和操作,在图中可以看到,Bool用于判定true/false,并且还可以进行对应的布尔运算
这些都是具体的例子,程序员在应用的时候会根据自己的需要来设计各种不同的数据类型:比如程序员需要数据类型T,于是程序员就会定义T的一系列操作,并且会先写规约再按照规约来实现
规约只规定了输入输出的准则,ADT也是只由操作定义,无关其内部实现
Types, whether built-in or user-defined, can be classified as
mutable or immutable .
数据类型按照其可变、不可变性可分为可变和不可变数据类型。
可变类型的对象:提供了可改变其内部数据的值的操作
不可变数据类型: 其操作不可改变内部值,而是构造新的对象
一些类型提供两种形式
还可以按照功能划分
▪ creator : t* → T 构造器( 从无到有)
▪ producer : T+, t* → T 生产器(从旧到新)
▪ observer : T+, t* → t 观察器
▪ mutator : T+, t* → void | t | T 变值器
这里是对于T,t的解释:
▪ Each T is the abstract type itself;
▪ Each t is some other type.
▪ The + marker indicates that the type may occur one or more times
in that part of the signature.
▪ The * marker indicates that it occurs zero or more times.
▪ The | indicates or.
构造器:可能实现为构造函数或静态函数
实现为静态方法的构造器通常称为工厂方法
mutator的返回值颇为多样化
1.Mutators are often signaled by a void return type. 变值器通常返回
void
2.A method that returns void must be called for some kind of side-effect,
since otherwise it doesn’t return anything. 如果返回值为void,则必然意
味着它改变了对象的某些内部状态。
3.变值器也可能返回非空类型
int is immutable, so it has no mutators.int不可变,所以int没有变值器
– creators: the numeric literals 0 , 1 , 2 , …
– producers: arithmetic operators + , - , * , /
– observers: comparison operators == , != , < , >
– mutators: none (it’s immutable)
▪ String is Java’s string type. String is immutable.String不可变,因此也没有变值器
– creators: String constructors
– producers: concat , substring , toUpperCase
– observers: length , charAt
– mutators: none
良好ADT 的设计:靠“经验法则”,提供一组操作,设计其行为规约 spec
规则1:设计简洁、一致的操作
It’s better to have a few, simple operations that can be combined in
powerful ways, rather than lots of complex operations.
– Each operation should have a well-defined purpose, and should have a
coherent behavior rather than a panoply of special cases.
– We probably shouldn’t add a sum operation to List , for example. It might
help clients who work with lists of integers, but what about lists of
strings? Or nested lists? All these special cases would make sum a hard
operation to understand and use.
设计一组简单操作,通过简单操作的组合实现复杂的操作。操作的行为应该
是内聚的。
规则2:要足以支持client 对数据所做的所有操作需要,且用操作满足client
判断方法:对象每个需要被访问到的属性是否都能够被访问到,例如list这个ADT:①必须要有get()操作,没有get()操作就无法获取list的内部数据。②
用遍历方式获取list的size –太复杂 vs 提供
size()操作,方便client使用。
规则3:要么抽象、要么具体,不要混合 — 要么针对抽象设计,要么针对具体应用的设计
(1)The type may be generic: a list or a set, or a graph, for example.
数据类型可以是概括性的,例如:表,集合,或者图
(2)Or it may be domain-specific: a street map, an employee database,
a phone book, etc.
或者数据类型是专注于某领域的,例如:一个街道地图,一个员工数据库,一个电话簿……
(3)But it should not mix generic and domain-specific features.但是一定不能把抽象概括的类型和专注于某领域的类型搞混了