软件构造Chapter6 Review

0.Outline

        第六章为Abstract Data Type,主要介绍抽象数据类型的基本概念、操作的类型、表示独立性与表示泄露、表示不变量RI、表示空间与抽象空间、抽象函数AF,以及如何在注释中写明RI、AF

1.ADT概述

        对于ADT我们并不陌生,从数据结构开始就有了关于ADT的种种说法。ADT作为面向对象的一个重要概念,与面向过程中数据与操作混合的思路相区别,我们可以说ADT是数据和操作的封装。但事实上,ADT的作用更在于将顶层功能与底层实现分离。

        举个例子,我们都熟知布尔代数中的true和false,我们对于true和false的需求仅在于用true和false表示某种状态,比如开、关,并使用在其上定义的操作,比如or、and等,但我们对于true和false在计算机中实际的实现方式并不感兴趣。在Java中,true和false可以被定义为boolean类型,C中true被定义为非0,false被定义为0,我们甚至可以使用字符串“true”和“false”来定义二者,无论底层实现如何,只需要保持顶层的功能不变即可。

        这就是为什么“ADT是由操作定义的”。

2.ADT类型及其操作

2.1.ADT的四种操作

        对于在ADT上定义的运算,也就是ADT的方法,可以视为ADT克林闭包上定义的某种映射或关系(具体请参考《离散数学》)。ADT的操作具体可以有很多个,但其类型无外乎以下四种:

        ①构造器Creator:Creator是指创建ADT的一类方法,这类方法可以是每个class中首先要写的构造函数,也可以是静态工厂化方法。这类方法可以使用一个或多个其他ADT的实例进行构造,但是不能使用自身来产生自身,这是其与Producer相区别的点。其形式化表示如下所示:

        上式将Creator视为一个从其他ADT的实例 t 的克林闭包到目标ADT的实例T的映射

        ②生产器Producer:Producer与Creator具有一定的相似性,都是产生一个目标ADT的实例,但二者的区别在于Producer必须以一个目标ADT的实例为输入。其形式化表示如下所示:

        可以看到,与Creator不同,Producer需要一个目标ADT实例的正闭包作为输入

        ③观察器Obeserver:Obeserver顾名思义,就是返回目标ADT在某一时刻某个特定状态的方法,比如常见的getName、getSize等都属于Obeserver方法,Obeserver方法的输入一般情况下为空,但也有特例。其形式化表示如下所示:

        ④变值器Mutator:Mutator顾名思义,就是通过输入对目标ADT实例的状态进行改变,一般情况下Mutator的返回值是void或boolean。其形式化表示如下所示:

2.2.两种ADT:Mutable vs Immutable

        上文中我们也简单介绍过mutable和immutable的概念,Mutable指的是“值”可以改变,Immutable指的就是“值”不可变。

        对于ADT而言,上述概念的理解就是其字面含义“值可变”或“值不可变”,“值可变”的Mutable类型容易理解和实现,毕竟运动是绝对的,静止是相对的,稍微动一下值就变了。

        注意到:这里的“值可变”指的是整个ADT的所有成员变量的值,范围还是很大的。

        如何让ADT的值保持不变还是有点挑战的,形式上,我们可以通过将全部成员变量定义为private来防止ADT外部对成员变量值的改变;然后再不提供Mutator类方法以防止ADT外通过方法对ADT成员变量值进行改变;还可以使用final关键字让ADT中的值是不可变的。如下图例子所示,上述步骤完成后,ADT貌似就已经“不可变了”

        看着貌似还挺不错,但事实上,注意charAt方法,该方法直接将a的地址传出(引用数据类型传递的是地址),这样外部得到了a的地址,想对其进行点什么有意思的操作也就是可能的了,这种情况就叫做表示泄露(rep exposure),为了防止表示泄露的发生,我们通常要额外注意Creator和Obeserver这两种方法,因为假如Creator方法是通过外部提供的Mutable类型进行初始化,我们很难保证外部程序不会对这个Mutable类型进行其他的操作,类似地,在Obeserver方法中,我们如果提供ADT内成员变量的地址,同样很难保证外部程序不会对它做出写有意思的修改。

        对于这种情况,一种直截了当的解决办法是把这些东西写在spec中,以甩锅的方式把这个问题留给client,但由于人性本恶,再精确的spec也不能保证不会有人对程序进行恶意的破坏,同样地,由于像某位大我们几旬的老教师那样科研科研不行、教学教学不行的人也不在少数,我们也不能保证client有阅读并正确理解spec的能力,所以说这种方法还不是很OK的。

        比较OK的方法就是靠人不如靠自己,上文中造成表示泄露的原因就是ADT中直接使用外部程序提供的引用或将自身成员变量的引用提供给了外界,而这种情况我们完全可以避免,比如在返回时,我们可以重新创建一个实例并将其返回给client,这种方法叫做defensive copy

3.RI与AF

        这一段同学们普遍看着比较蒙,但这段内容其实并不难,只不过是PPT上不怎么说人话,这段搞明白了考试20+到手,有挂科风险的同学可以先看这段(不过u1s1这门课真不太容易挂科)。

        PPT上和实验中经常让人迷惑的无外乎这几个概念:RI、AF、rep,下面我们来细掰扯掰扯这几个概念。

        首先,就像上文提到的一样,ADT存在的意义就是在于在底层数据和顶层操作之间形成一定的隔离,让client使用ADT的功能即可,不要知道ADT是怎么实现的,这就好比某些同学考试只写个结果,步骤一点都不写,然后跟老师说:“我写出来结果就行了呗,你非得让我写出来我怎么算的干啥”,这些同学应该会很喜欢面向对象的编程方法。

        按照这个思路,就形成了下图所示的结构,ADT成为了用户与开发者之间的屏障(没有这道屏障开发者写的翔山被用户看见了也不太好)

        ADT这个圈,也就将一个空间分为了两个部分:用户可见的部分和仅开发者可见的部分。

        用户可见的部分叫做抽象空间 A ,因为这个空间中所有的概念都是抽象而成的,与底层实现无关,就好比你觉得你在用Boolean,这个“你觉得的Boolean”就是抽象空间的内容,换句话说抽象空间其实就是开发者给用户形成的“假象”,开发者让用户觉得这个东西是这么回事,但实际未必  

        仅开发者可见的这部分空间就叫做表示空间 R ,也就是我们常说的rep,这部分是ADT底层真正的实现,就比如我上文提到的Boolean,ADT中可能就是用String来实现的,String它就对应于表示空间。

        说到现在,你应该已经理解了第一个概念:rep,下面我们来说另外两个

        现在我们有了两个空间,表示空间 R 和抽象空间 A,还是以上文中的Boolean为例,String为表示空间的内容,Boolean为抽象空间中的内容。显然,二者是存在着某种联系的,我们在ADT中需要一个String类型的成员变量来代表true,也需要一个String来代表false,这种所谓的“代表”其实就是一种映射关系,而这种将表示空间中的内容映射到抽象空间中的内容的映射,就称为抽象函数(Abstract Function, AF)。

        换句话说,抽象函数AF描述了表示空间与抽象空间中值的对应关系,这个映射不一定是单射,但一定是满射。这个概念在我们当前的例子中也好理解,你总不能给用户提供一个只有true没有false的Boolean类型变量吧(不过这也是件好事,比如老师批卷时候)。注意到:AF并不是给用户看的,它是写在ADT内部给开发者看的,因为你并不想让用户知道你底层代码写的有多烂。

        好,经过我这么一番讲解(扯淡)后,你应该已经理解了AF和rep,下面来说最后一个概念RI

        其实顺着刚才的思路走,AF是一个函数,它值域已经给定了(用户需求确定AF的值域),那是不是也得有个定义域呀,RI就是来确定这个定义域的。还继续我们上文的例子,所有String构成的集合理论上讲是一个可数无限集,那么问题来了,值域Boolean好像只有两个呀。那么我们就需要对String这个集合进行限定,规定出哪些值对于AF来说是合法的,也就规定了AF的定义域,这就是RI的功能。

        OK,说了这么多,最后都不考,考的东西是让你怎么写(填选可能会考定义):

        这个是我写的RI和AF,其实刚才说了那么多,最后落到笔头还是挺容易的,AF就是把你各个成员变量都是干嘛的、这个ADT是干嘛的写清楚;RI说白了就是让你把成员变量取值范围写清楚。然后后面还有个Safety from rep exposure,这个也好说,也是随便说说你怎么保证不会发生表示泄露的。

        最后,注意到:不要将RI、AF、SRP(Safety from rep exposure)与spec搞混,RI、AF、SRP是针对ADT说的,spec是针对方法说的,一定不要搞混。

        这章要说的基本上说完了,最后祝愿被某位大我们几旬的老教师折磨的同学们早日摆脱魔爪,考试考出个好成绩,评教时候千万别忘了要如实评价啊!

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值