Java的抽象数据型

除了编程语言所提供的基本数据类型和对象数据类型,程序员可定义自己的数据类型。

1数据抽象

定义:由一组操作所刻画的数据类类型。
例:一个number可以进行addmultiply
传统的类型关注于数据的具体表示。抽象类型强调作用于数据类型的操作。程序员和用户不必关注数据是如何存储的,是需要设计和使用操作即可。
ADT是由操作定义的,与其内部是如何实现的无关。

2.类型之间的分类以及操作

java中的类型(无论是内置的还是用户定义的)分为可变类型不可变类型
可变类型的对象:提供了可改变内部数据的值的操作。例:Data是一个可变类型的对象,因为有方法setMonth()可以改变内部数据的值。
不可变数据的对象:其操作不是改变内部值,而是创造一个新的对象。例:String不会修改原有的字符串,而是创造一个新的String。
有时一个类型也会以两种方式(可变和不可变类型)提供对象,例如字符串的String和StringBuilder。

分类一个ADT(抽象数据型)的操作:
Creators:Creators能够通过一些必需的参数来构造一个新的对象,即从无到有。(构造器)
Producers:Producers能从一个对象产生一个新对象(相同的对象)。例如String的concat()操作能够从两个String中合并产生新的String。(生产器)
Observers:Observers通过观察一个抽象类型的对象来返回一个不同于观察对象的一个新对象。例如List的size()。(观察器)
Mutators:用于改变对象属性的方法。例如List的add()。(变值器)
注:如果有Mutators方法的话,那么数据类型必然是可变类型的。

可变类型的例子:
List
creators:ArrayList 和LinkedList的constructor,Collections.singletonList。
producer:Collections.unmodifiable.List
observers:size,get
mutators:add,remove,Collections.sort

不可变类型的例子:
String
creators:String constructor。
producer:concat,substring,toUpperCase。
observers:length。
mutators:没有。因为是不可变数据类型。

3.设计抽象数据类型

设计好的ADT,靠“经验法则”,提供一组操作,设计行为规约spec。
规则1:设计简洁,一致的操作。
比起许多复杂的操作,不如结合有用的方法的简单的操作。每一个操纵都应该有明确的目的。并且有一致的行为而不是一大堆的特例。
规则2:要足以支持用户对数据所做的所有操作的需要,而且设计操作的难度要低。

4.表示独立性

定义:用户使用ADT时无需考虑内部是如何实现的,且ADT内部表示的变化不应影响外部用户的使用。
例如:操作说明中用到List的时候不要用LinkedList和ArrayList代替。
除非ADT的操作指明了具体的pre和post-condition,否则不能改变ADT的内部表示。spec规定了client和implemanter之间的契约。

5.测试一个抽象数据型

测试creators,producers,mutators:调用observers来观察这些operetions的结果是否满足spec。
测试observers:调用creators,producers,mutators等方法来产生或改变对象,来看结果是否正确。
风险:如果被依赖的其他方法有错误,可能导致被测试方法的测试结果失效。

6.不变量(invariants)

一个好的数据型最重要的属性就是保持不变量。
定义:在一个程序中在任何时候都是true的属性。例如:immutability就是一个典型的不变量。
由ADT来负责保持不变量,与用户端的任何行为无关。
不变量需要的原因:保持程序的正确性,容易发现错误。总是要假设client有恶意破坏ADT的不变量。
保证不变量的首要条件:把所有的属性都设置成private,而不是public。
如果用户能够直接接触一个类型的属性的话,那么就造成了表示泄露(representation exposure)。不仅影响不变性,也影响了表示独立性:无法在不影响客户端的情况下改变其内部表示。
除非迫不得已,否则不要把希望寄托于客户端桑,ADT有责任保证自己的invariants,并避免表示暴露。最好的方法就是使用immutable的类型,彻底避免表示暴露。
总结:
1.返回一个可变类型时候,要返回复制的对象。或返回本体的unmodifiable版本。
2.保持不变性和避免表示泄露,是ADT最重要的invariants。

7.表示不变量(Rep Invariants)和抽象功能(Abstraction Function)

Abstraction Function:
两个值域的空间:
R:表示空间。由实际的实体的值组成。(一般情况下ADT的表示比较简单,有些时候比较复杂)
A:抽象空间。clients看到和使用的值。
ADT开发者关注空间R,client关注抽象空间A。
R与A之间的映射关系:
满射:设立抽象类型的目的是支持对抽象类型的操作。
未必单射:可能有多个引用指向同一个对象。
未必双射:可能在表示空间(R)中有不符合规则的元素。
抽象函数:R和A之间映射关系的函数,即如何去解释R中的每一个值为A中的每一个值。(R中的部分值并非是合法的,在A中无映射值)
Rep Invariant:
将R中的每一个值映射为true或false。
RI(r)是true当且仅当r在A中有映射值。
**可以把RI看成是AF的一个子集。**包含了所有合法的表示值。

AF and RI
这两个的说明在类型中属性的下面,方法的上面。不同的内部表示,需要设计不同的AF和RI。
选择某种特定的表示方法R,进而指定某个子集是“
合法的”,并为该子集中的每一个值做出“解释”(AF),即如何映射到空间中的值。

设计ADT的步骤:
(1).选择R和A。
(2).RI–合法的表示值。
(3).如何解释合法的表示值–映射AF。
做出具体的解释:每个rep value如何映射到abstract value。
对于用户来说,需要知道一个ADT的:
1.Abstract value space
2.所有的public方法。

随时检查RI是否满足要求:
设置一个checkRep函数,随时检查不变量是否有所改变。在所有可能改变rep的方法内都要检查。

8.有益的可变性

对immutable的ADT来说,它在A空间的abstract value应该是不变的。
但其内部表示的R空间的取值则可以是变化的。

博客总结:

**ADT的规约里只能使用client可见的内容来撰写,包括参数,返回值和异常等。**如果规约里需要提及值,只能使用A空间里的值。
ADT的规约里不应谈及任何内部表示的细节,以及R空间的值。内部的private对外部应该是严格不可见的。
构造器和生产器在创造对象时要确保不变量为true。变值器和观察器在执行时必须保持不变性。在每个方法return之前,用checkRep()检查不变量是否得以保持。
1.抽象数据型由它们的方法(操作)组成。
2.操作能够被分类成Creators,Producers,Observers,Mutators。
3.一个ADT的规约是一组操作和它们规约的集合。
4.一个好的ADT是简单的,连贯的,完整的和表示独立的。
5.一个好的ADT是好理解的,它把具体的实现隐藏起来,用户只需要知道需要输入的参数,功能和返回值即可。
6.一个好的ADT可以进行更改。仅更改R空间中的数据,不会影响用户的使用。
7.一个不变量是在一个类型的一直保持true的属性。
8.为了检验不变量,设置一个checkRep()函数,在每一个可能破坏不变量的方法中在最后进行检查。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值