对于AF、RI、Safety from rep exposure、spec的归纳总结
每次写实验时,在写代码之前都要进行AFRISafety from rep exposure spec的编写,过程十分繁琐,但是也非常有用。根据他们写代码,不仅可以找到切入点,而且思路更加清晰了,避免了许多bug的产生,更加满足客户端的要求。于是在此对他们做出总结归纳。
1.Spec
简介
Spec是程序员在设计ADT时对自己所写方法的规约,它规定了方法应该做什么,不应该做什么。而在接下来的设计中,测试用例的编写就需要依靠Spec的描述,因为程序员所编写的代码必定是符合spec的,否则就是不合格的。同时,有了Spec的存在,客户端在使用代码时就会有所依据,好的Spec可以大大节省客户端使用自己的API时所需要的时间,并且大大降低了客户端对自己所编写的代码的误解。
主要内容
Spec的主要内容分为以下三部分:
1. 前置条件precondition:这是对客户端的约束,是用户在使用方法时必须满足的条件。在Java中一般使用声明@param说明每个参数的前置条件。
2. 后置条件postcondition:这是对开发者的约束,也是方法结束时设计者必须满足的内容。在Java中一般使用如下两种声明,其内容及用途如下:
@return:对后置条件的说明,一般为返回值;
@throws:说明出现异常的时候会发生什么,一般为异常处理方式。
spec强弱
若想要Spec变强,可以采用如下方式:
1. 更宽松的前置条件
2. 更严格的后置条件
有些spec的强弱是无法比较的,没有强弱之分。
注意事项
程序员不应该让用户知道方法的内部逻辑是怎么样的,以避免恶意用户对代码结构的破坏。
2.AF和RI
两个空间:表示空间,抽象空间
1.表示空间里面包含的是值具体的实现实体。一般情况下ADT的表示比较简单,有些时候需要复杂表示。
2.抽象空间里面包含的则是类型设计时支持使用的值。这些值是由表示空间抽象出来的,也是使用者关注的。
3.ADT实现者关注表示空间,而用户关注的是抽象空间。
4.R->A(表示空间->抽象空间)映射特点
(1)每一个抽象值都是由表示值映射而来 ,即满射:每个抽象值被映射到一些rep值
(2)一些抽象值是被多个表示值映射而来的,即未必单射:一些抽象值被映射到多个rep值
(3)不是所有的表示值都能映射到抽象域中,即未必双射:并非所有的rep值都被映射。
我们用一个表示分数的ADT来举例,该ADT的rep为x(分子),y(分母):
例(1)抽象空间的所有分数都可以由表示空间的x/y来表示
例(2)对于两组不同的x,y其约分后可能表示为同一个分数
例(3)非法rep,例如y=0,则在抽象空间内没有对应的映射
AF
全称Abstraction function(抽象函数),类似于一个映射
其中R是创建的类,A是抽象值构成的空间
就是构造的程序的 ADT里面的量 到 外部人员想要操作的量 的映射
AF一定是满射,不一定是单射,也就不一定是双射
(也就是R中的类不一定能找到A,但是A中的抽象必须有R中对应的类)
RI
RI全称是:Representation invariant(表示不变量),用来表示“什么样的类满足抽象”
以上图为例,“abbc”就是不满足RI的元素,一个满足这个图的RI就是“不可以有重复的字母”
也可将RI看作:所有表示值的一个子集,包含了所有合法的表示值
也可将RI看作:一个条件,描述了什么是“合法”的表示值
(AF,RI的具体实例参考实验2的代码)
注意事项
RI、AF存在如下关系:
相同的R可能存在不同的RI;
即使是同样的R、 RI,也可以根据解释不同,而获得不同的AF;
两个ADT有相同的rep和相同的AF,但其R也不—定相同。
不应该给client看的内容有AF、RI、Rep exposure safety argument、testing strategy、Rep、Implementation和Test cases等等,留给client的只有Spec。
可以通过写checkRep()随时检查RI
3.Safety from rep exposure
防止表示泄露的方法
通常有以下几种方法:
1.用private和final关键字对其进行修改:------即将类中所有的属性(变量)定义为private类型,目的是不让用户得到你的内部属性
2.尽量使用immutable数据类型,比如能使用String就不使用StringBuilder,能使用Instance或
LocalDateTime就不使用Data
3.返回值使用防御式拷贝