Spec,既方法的设计规约(Specification)。
(1)为什么要设计规约?
1.没有规约,在团队合作时无法分配任务,无法写程序,即使写出来,也不知道对错
2.可以使程序与客户端之间达成一致,明确双方的责任,并且对正确实现进行定义。
3.规约给供需双方都确定了责任,在调用时,双方都要遵守
(2)规约的作用:
1.精确的规约,有助于区分责任
2.客户端无需阅读调用函数的代码,只需理解spec即可
3.规约可以隔离“变化”,无需通知客户端
4.规约也可以提高代码效率
规约相当于在调用者与实现者之间建立一道防火墙,调用者无需知道方法内部实现,了解规约进行调用即可。
也就是说,只要满足规约,实现者便可以对代码进行修改,如果两个方法内在实现不同,但是可以满足相同的规约,那么就可以称这两个方法具有行为等价性。
(3)行为等价性:
是站在客户端视角看行为等价性的。
可以根据规约判定是否行为等价,而与其具体实现无关。
(4)规约的结构:前置条件和后置条件
1.前置条件:对客户端的约束,在使用方法时必须满足的条件
2.后置条件:对开发者的约束,方法结束时必须满足的条件
3.契约:如果前置条件满足了,后置条件必须满足
注意:如果前置条件不满足,则方法可做任何事情。
(5)Java中的规约:
静态类型声明是一种规约,限制前置条件和后置条件,可据此进行静态类型检查static checking。
方法前的注释也是一种规约,但需人工判定其是否满足。
前置条件:@param
后置条件:@return 和@throws
在我们了解AF与RI之前,我们需要了解一些基础知识。
在实现ADT时,我们会使用如下两个空间:
R:表示空间(rep values),即实现者看到和使用的空间
A:抽象空间(abstract values),即客户端看到和使用的空间
对于R与A满足
R到A:一定满射,不一定单射
AF,既抽象函数(Abstraction function),R->A
AF描述内存到实际的映射,所以AF一定是满射,但不一定是单射,也就不一定是双射。
RI,即表示不变量(Representation invariant),R → boolean
RI告诉我们空间R中的r是否被AF映射到了空间A中的某个值,如果把R中满足RI的元素形成一个子集,那么这个子集中所有元素均被AF映射到了空间A中
在上图中,RI可解释为“字符串中没有重复的字母”
AF和RI与内部表示的设计息息相关,不同的内部标识,需要设计不同的AF和RI;同一种表示可能有不同的RI,进而有不同的AF;即使同一种R,同样的RI,也可能有不同的AF,即“解释不同”
首先我们要知道一个名词,表示泄露(representation exposure):指类外的某个代码可以直接修改表示形式
而Safety from rep exposure,即表示泄漏的安全声明,就是对表示泄露安全相关的参数,特别是输入参数和返回值,给出保证不泄露内部表示的策略
1.All fields are private( final)------即将类中所有的属性(变量)定义为private类型,目的是不让用户得到你的内部属性
2.尽量使用immutable数据类型,比如能使用String就不使用StringBuilder,能使用Instance或LocalDateTime就不使用Data
3.如果我们使用了mutable类型的数据,为了防止对外泄露其内部表示,需要使用defensive copy,即防御式拷贝。如果是Set、Map等数据类型,还可以使用Collections.unmodifiableSet,Collections.unmodifiableMap等方法。
4.随时检查RI是否满足,自写checkRep()方法(通常在其中使用assert来进行判断),在创造和改变表示的方法中(creators,producers,mutators),进行调用,来确保不变性。