【软件构造HIT】ADT与OOP相关知识准备——数据类型和规约

ADT与OOP相关知识准备(Chapter4、5总结)

  • 数据类型与类型检验(Chapter4)
  1. Java语言中的数据类型

①基本数据类型:如int,long,boolean,double,char等

②对象数据类型:如String

注:容器类型(如List,Map,Set)其操作的元素要求是对象类型,所以需要对基本数据类型进行封装,如将int封装成Integer。

  1. 可变与不可变

①改变一个变量/改变一个变量的值

改变一个变量:将一个变量指向另一个内存地址空间;

改变一个变量的值:将该变量当前指向内存空间的写入一个新的值。

②不变性和immutable类型:

不变性:在允许的情况下,尽可能使这个变量不变;

Immutable类型:一旦对象被创建,其值不能改变;

注:如果对象是引用类型,也可以是immutable的,这时表示一旦确定指向的对象,那么就不能在改变引用类型变量的指向。

③immutable对象和mutable对象:

Immutable:一旦被创建,无法修改自己的值/引用;

Mutable:拥有方法(mutator)可以改变自己的值/引用。

  1. Snapshot Diagram

用来表示程序运行时的内部状态,属于软件三维视图中的Run-time,Moment,code-level的内容,具体绘制有如下规则:

①基本类型的值不用加任何形式,如图所示:

②对象类型的值需要用一个圈,圈起来,如图所示:

其中x和y是int类型的变量,属于基本数据类型,不用加任何形式,x指向5,y指向-3;

③将变量名写在外面,如果对象类型是immutable对象,则用双线椭圆表示不可变,如下图所示:

其中s为变量名,并且由于String是不可变类型的对象,在将s的值由“a”修改成“ab”时,需要重新创建一个String类型的对象其值为“ab”,再修改变量s的指向;

④不可变的引用:带final关键字修饰,在Snapshot Diagram中用双线箭头来表示,如下图所示:

总结:Snapshot Diagram是本课程分析程序运行结果时的重要分析方法之一,在后续许多代码运行结果的分析中都会用到这个方法,需要我们将这个方法扎实地掌握。

下面用一道例题检验对于Snapshot Diagram的掌握情况:    

题目:

解答:

  1. 常用的不可变数据类型

①基本数据类型及其封装对象类型:int,Integer等;

②不可变的包装:unmodifiableList,unmodifiableMap,unmodifiableSet。这三个不可变的封装在后续避免表示泄露以及防御式编程部分都有着重要的应用。

下面给出一个在使用不可变的包装时很容易犯下的错误:

对于图中所示示例,我们可以很容易地看出会在第四行发生编译错误,因为我们不能修改一个进行了不可变包装后的list,但如果我们去掉第四行,运行结果会是多少呢?

如果按照我们下意识的想法,我们的listCopy是不能改变的,那么listCopy中就应该只有“ab”一个元素,那么listCopy的size就应当为1。但是出乎我们意料的事情发生了:运行后得到listCopy的结果为2!这是为什么呢?

原因很简单:在进行不可变的包装后,list和unmodifiableList(list)指向同一个内存地址空间,用Snapshot Diagram表示如下图所示:

我们虽然使用不可变包装保证了不可以通过listCopy修改这一地址空间的值,但是由于list是mutable的,我们可以通过list的方法来修改这一地址空间的值,也就同时修改了listCopy中的值。

那么这样的话,我们的不可变包装就变成了无效的包装了。所以我们如何避免这种情况的发生呢?

所以我们在返回一个unmodifiable对象后一定要防止用户对于原始的mutable对象进行操作,要把对于原始mutable对象的引用屏蔽掉!

  • 设计规约(Chapter5)

本章中主要学习了如何设计好的规约和如何判断两个规约的强度。

  1. 规约的具体构成部分

①前置条件pre-condition(对于client的约束):用require+对输入的要求表达;

②后置条件post-condition(对开发者的约束):用effects+对输出的要求表达;

③异常行为:个人认为也可以认为属于pre-condition的一部分;

④方法签名:包括方法的可见性、返回值类型、方法名、参数列表等信息。

  1. 如何设计好的规约

规约设计一般情况下遵循如下原则:

①规约中不要写参数类型和返回值类型,因为方法签名中有(个人认为写进去还有泄露属性的风险);

②规约中不要包括有关局部变量和类的私有属性的内容;

③除非在后置条件中声明过,否则方法内部不应该输入参数;

④内聚的:规约描述功能单一、简单、易理解;

⑤信息丰富:规约准确,无二义性;

⑥规约中尽量使用抽象类型,增强方法泛化能力;

⑦不限定太强的pre-condition,而是在post-condition中抛出输入不合法的异常,同时要尽可能在错误的根源处抛出异常。

  1. 判断规约的强弱

①规约强度S2≥S1需要满足的条件:

  1. S2的前置条件弱于S1或相等;
  2. S2的后置条件强于S1或相等(此时需要在满足S1的前置条件下进行比较)

这时S2的规约强度不弱于S1,在使用方法S1的地方都可以使用S2进行安全替换。

下面用例子来巩固我们对于规约强度的判断:

在这个例子中,最上面的为最初的规约。第二个规约同最初的相比,两个规约的后置条件是相同的,但第一个前置条件要求val只能出现一次,而第二个前置条件要求val可以出现一次或多次,第二个前置条件更加宽泛(更弱),所以根据规约判断的方法我们可以得出第二个规约强于第一个规约。再将第三个规约与第二个规约相比,两个规约的前置条件是相同的,但是最后一个规约对于后置条件返回的下标要求是最小的,所以最后一个规约的后置条件强于第二个规约,也因此最后一个规约的强度要强于第二个规约,第二个规约的强度要强于最初的规约。

②用图来表示规约的强弱:

  1. 某个具体实现若满足规约,则落在该规约对应的椭圆范围内,否则在其之外;
  2. 更强的规约,表示为更小的区域:
  1. 更强的后置条件→实现的自由度更低→表示在图中面积更小;
  2. 更弱的前置条件→实现时要处理更多的输入→实现的自由度低→表示在图中面积更小。

用一个例子来巩固用图来表示规约强弱的知识:

分析:由于methodA()和methodB()都是一个具体的实现,并且两个方法的规约强度相同,对于client端无需关注方法如何实现,methodA()和methodB()在client端使用没有区别,A正确;methodD()的区域包含了methodC()的区域,说明在使用methodD()的地方,都可以用methodC()进行安全的替换,并且由于规约强度越高对应的区域越小,可以判断出methodC()的规约强度大于methodD(),B正确;methodA()和methodC()强度都大于methodD(),C正确;methodD()的规约强度大于methodE(),可以用methodD()安全替换methodE(),但是不能用methodE()安全替换methodD()。D错误。

通过对题目的分析,我们更加扎实地掌握了对于规约强度判断的内容。以上也就是在正式学习ADT和OOP需要的前置知识,对于Chapter4和Chapter5的主要内容进行了一个总结。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值