设计ADT(抽象数据类型)过程中的若干概念与心得体会

1.引言

在面向对象的设计软件的过程中,很重要的一点就是设计一个好的ADT,也就是抽象数据类型。当然,设计软件时,从用户要求出发,进行整体工程的架构是更为宏观层面的设计,在这一部分中需要考虑到怎样利用面向对象的特性来满足软件的复用性,可维护性(可扩展性),怎样设计各个ADT之间的关系,比如继承关系,委派关系,或者使用一些设计模式,这些我们以后再谈,在今天这篇文章中,我们谈谈更加微观的,就一个单独的ADT设计过程中的相关概念以及一些个人感受。有了这样设计单个ADT的基础,我们才能更好的理解ADT从而构建ADT之间的关系做更高层次的架构。

2.ADT中的若干概念

(1)specification——spec

        specification意为规则,规格,简写为spec,spec声明了你设计软件时必须遵守的规则和必须实现的任务,其中如果一个spec相对于另一个需求更少而功能更强,我们就说这个spec覆盖了另一个,此时这个更强大的spec可以替换另外一个,因为它完全能够实现原来spec的功能。在你开始编程前,你对这个ADT需要完成什么功能应当设计完成,所以我们可以现在这个类中写出各个方法,注意此处的方法可以不必具体实现。针对每个方法需要给出其明确的spec——输入的类型,要求,以及怎样获得输出。

        注意spec是在具体之前就设计好的,因为要根据每个方法的spec来写对应的测试代码,这又是另外一种思想——测试优先的设计思路。也就是说,在设计ADT时,我们在实现一个方法或一个类之前,要先写好它的测试代码,这样的好处是,第一避免我们写完具体实现代码十分自信懒得再写测试代码,第二我们每写完一个方法可以立即进行测试,方便我们找到自己代码中的bug,第三优先设计测试可以避免我们的实现思路干扰我们的测试思路,进一步提高我们测试的完备性,为我们最终代码的正确性提供保证。下面是一个例子:

 (2)repository——rep

        repository意为贮藏室,在软件构造中它代表一个类中的数据,就是在class下面定义的那些(通常由构造方法给出)。一个类的rep也应当在设计环节就敲定,但注意这里的rep是我们ADT内部实现时用到的数据结构,比如可能是数组,是链表或者更加复杂的结构。但ADT名为抽闲数据类型,就是说他提供给用户的是一个完全抽象的数据类型,用户只需要关注它的方法来使用它,而完全不关心实现。也就是说,我们的rep尽可能的不能让用户知道,更不能让用户改变!!!这点非常重要以至于后面还有一条专门说这个。

(3)Representation invariant——RI

        RI是一个类中的不变量。需要注意的是,虽然RI被译为不变量,但它不必是一个具体的值,RI可以包含若干条规则,这些规则是自这个类创建到它被删除都必须始终满足的,如果不满足就是错误。比如一个表示物体长宽高的类中,我们不可能让其中一项为负数,如果是那就是错误了。我们可以将RI具体的通过注释写在我们的ADT中,这或许有些麻烦并且枯燥,但是这确实有其必要性。明确的写出RI有助于我们明确清晰自己的思路,防止我们犯一些低级错误。

(4)Abstraction function——AF

        AF是一个抽象函数,正如我们之前所说的,ADT提供给用户的是一个完全抽象的数据类型,用户不知道更不关心你在内部怎样实现,而我们程序员是面向自己设计好的内部的rep在编程,而AF就是那个将抽象类型与rep联系起来的桥梁。所谓抽象函数就是写明你的rep中的每个变量在抽象的数据类型中反映了什么,你需要单独写一部分注释来说明这种对映关系,同RI中我说过的,这一部分注释也是必要的,有助于我们更好的编程。这个概念可能有些难理解,我们举个例子,比如你设计了一个ADT,其作用描述一个人的姓名,年龄等,那么我们的rep中就要有对应的一个字符串变量,一个整型变量等,而我们的ADF就是要说明:这个字符串对应一个人的名字,那个整型变量对应一个人的年龄,以此类推。

(5)Safety from rep exposure

        这一部分就是我们在第二部分rep中提到过的,rep不能被用户知道,更不能被用户改变,如果发生了用户改变rep的情况,我们就称之为表示泄露,是非常严重的错误。那我们首先说明一下表示泄露的可能性,实际上表示泄露是由于我们在rep使用的某些数据结构是可变的,比如列表,集合,字典等,这样如果我们直接返回一个可变的数据结构本身,相当于返回一个指向这些数据首地址的指针,用户根据这个指针就可以改变我们的数据。

        我们常见的防止表示泄露的方法包括防御式拷贝,以及将内部变量设为static类型等,有关这些方法的具体内容读者可以另行查阅,并不十分困难,这里仍然需要你用单独的一部分注释阐明你怎样在这部分ADT中避免表示泄露,其作用是帮助你检查是否出现表示泄露,并且给你和使用你ADT的人一定的信心。

(6)checkRep

        我们需要编写一个checkRep方法,在这个方法中我们使用断言来保证我们在RI中规定的每一条必须得到保证的规则都没有被违反。

        在写完checkRep后,本ADT中的每个方法在结束前(各个分支)都需要调用一次checkRep方法来确保这个方法运行后没有违反RI。可能有些方法显著的不会违反RI,使得你认为调用checkRep是不必要的,但为了保险起见,我们还是在每个方法都使用一次,防止自己犯糊涂。下图为一个checkRep方法的例子:

3.对于ADT设计的心得体会

        事实上设计ADT是一个比较复杂的工作,作为一名初学者,绝大多数情况下我也是在给定的框架下进行有一部分自主性的设计而不是从头开始全部自己设计。即便如此在完成这些程序的时候我也有一些体会,感受。最初学习到这些与ADT相关的知识,我还算理解了RI,AF这些相关的概念,但我对为什么要把它们以注释的方式写在程序中非常不解,我感到这非常麻烦和枯燥,因为一个工程中往往有好几个ADT,它们每个都有一堆注释需要你完成,而你本可以直接写完实现代码了事。但随着学习的深入,我渐渐感受到了这样做的价值,之所以这样说有两个原因。其一是和过去直接写代码的纵向对比,在代码中写明RI,AF这些规则固然耗费时间,但之后编写程序时我确实感到思路更加清晰顺畅,每当你对自己某个变量的含义产生疑惑,你都可以在上面的注释找到对应的解释,而checkRep方法可以说就是照着RI一条一条来验证的,所以这些工作我个人认为是十分有其必要性的。这些概念被明确的提出就是为了我们更加正确高效的编程。

        虽然这里单独列了第三部分说体会,但好多都已经在第二部分中提到了,这里就不重复了。这篇文章说到底只涉及了ADT内部的各个概念,算是软件设计最初学习的内容,我写这篇博客既是复习也是让自己明确一下学过的知识,之后可能会总结后面更宏观的设计方法,比如一棵ADT树中上下ADT的继承关系,多个ADT之间的委派关系以及,接口,设计方法等其他概念。

        

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值