什么是抽象
用户定义类型
类型分类与操作
无论是用户自己定义的还是内置的类型,都可以被分为可变的和不可变的。可变类型的对象可以被改变,也就是说,他们提供的操作在执行时会导致在同一个对象上的其他操作给出不同的结果。如Date是可变的,我们可以调用setMonth改变一个对象,并使用getMonth观察变化。String是不可变的,因为他的操作创建了一个新的String对象而不是对现有的String对象进行改变。有时一种类型将以两种形式提供,一种是可变的一种是不可变的。如StringBuild就是String的一个可变版本。
每个T都是抽象类型本身; 每个t都是其他类型。+标记表示该类型可能在该签名的该部分中出现一次或多次,并且*标记指示它出现零次或多次。| 表示或。
一个创建器操作通常作为构造函数实现,如new ArrayList()。但是创建器也可以作为一个简单的静态方法,如Arrays.asList()。实现为静态方法的创建器一般称为静态工厂,Java中的各种String.valueOf方法都是作为静态工厂实现的。
变异器通常由无效的返回类型来设置。返回void的方法在调用时会产生一些副作用,因为它不返回任何东西。但不是所有的变异器都没有返回类型,如Set.add返回一个布尔型的值,只是该集合是否加入了新的元素。
抽象数据类型实例
下边是一些抽象数据类型的例子以及他们的一些操作
设计抽象数据类型
设计一个抽象的数据类型涉及选择一个好的操作并确定他们的表现方式。下边是一些经验法则。
最好有几个可以以强大的方式进行组合的简单的操作,而不是进行大量复杂的操作。
每个操作都应该有一个明确的目标,应该有一个连贯的行为,而不是一堆特殊情况。例如,我们可能不应该向List添加一个sum操作。虽然它可以帮助客户处理全书整数的List,但对一个全是字符串的List或嵌套的List就不适用了。所有这些特殊情况都会导致sum操作难以理解和使用。
这套操作在一定程度上应该足够的,因为必须有足够的操作来完成客户端可能想要做的各种计算。一个好的测试是检查是否可以提取该类型对象的每个属性。例如,如果没有get操作,我们将无法找出List中的元素。基本信息应该很容易获得。例如,List中的size方法并不是必须的,因为我们可以应用get方法来增加索引,直到失败,但这样做效率不高并且极其不方便。
该类型可能是通用的,例如列表或集合或图表。或者它可能是特定领域的:街道地图,员工数据库,电话簿等,但它不应该混合通用和特定于域的功能。用于表示纸牌序列的Deck类型不应该有一个通用的add方法来接受像整数或字符串这样的任意对象。相反,将诸如dealCards这样的特定于域的方法放入泛型类型List中是没有意义的。
代表独立
一个好的抽象数据类型(ADT)的关键应该是独立于表示的。这意味着抽象类型的使用与其表示形式(用于实现它的实际数据结构或数据字段)无关,因此表示形式的变化对抽象类型本身之外的代码没有影响。例如,List提供的操作与列表是以链表还是以数组表示无关。除非通过前置条件和后置条件充分指定了操作,否则您将无法更改ADT的表示形式,以便客户知道要依赖哪些内容,并且知道可以安全更改的内容。
这种实现方式的一个问题是,它放弃了提高性能的机会。由于这种数据类型是不可变的,因此子字符串操作并不需要将字符复制到新数组中。它可以指向原始的MyString对象的字符数组,并跟踪新的子字符串对象所代表的开始和结束。
由于MyString的现有客户端仅依赖于其公共方法的规约,而不依赖其私有字段,因此我们可以在不检查和更改所有客户端代码的情况下进行更改。这是代表独立的好处。
总结
抽象数据类型的特点是其操作。
操作可以分为创建器,生产器,观察器和变异器。
ADT的规约是其一套操作和这些操作的规约。
一个好的ADT是简单的,连贯的,充分的,和表示无关的。