软件构造第三章学习笔记及心得(二)

软件构造这门课是比较具有挑战性,知识点多且琐碎的,因此,我将结合ppt和回放,对软件构造的知识点来进行整理,主要用于对于知识点的记忆和复习,如果能够对你有所帮助,那荣幸之至。

在这篇博客中,我将对第三章 3.2 这节的知识点来进行整理总结。

3.2 设计规约

3.2.1 方法

  • 方法参数和返回值的类型匹配检查在静态类型检查阶段完成
  • 方法是程序的积木,应该是独立的,抽象(封装)的。
  • 使用方法的客户端,无需了解方法内部具体是如何工作的

3.2.2 规约

代码中蕴含着程序员的设计决策,比如说final关键字定义了设计决策:不可改变。但是,这种隐式的设计决策是给编译器看的。而规约,简单来说就是描述方法的输入输出应该是什么,则是给自己和他人所阅读用的。
规约的好处:

  • 方便自己和他人理解代码
  • 团队合作的基础
  • 给供需双方都确定了责任
  • 客户不需要理解方法,只要读懂规约就行
  • 判断程序的对错
  • 充当防火墙的角色,隔离变化

注意,规约中不能够有方法的具体实现细节,也就是说只能说明这个方法是做什么的,而不能透露方法怎么做

3.2.2.1 行为等价性

  • 行为等价性即判断两个方法能否相互替换
    判断行为等价性的时候,应该站在客户端的视角来看,并且根据规约来判度是否行为等价。
    例如下面两个函数在给出的规约下是行为等价的
    软件构造第三章学习笔记及心得(二)
    软件构造第三章学习笔记及心得(二)

3.2.2.2 规约的结构

规约结构可以分成三块:

  • 前置条件:对客户端的约束,调用方法时必须满足前置条件
  • 后置条件:对开发者的约束,方法结束时必须满足的条件
  • 异常:方法会通过throws往上抛的异常

隐含契约:如果前置条件满足,则后置条件一定满足,如果前置条件不满足,则后置条件也可以不满足。但程序还是最好优雅的结束

3.2.2.3 Java规约

每个变成语言规约的格式都不同,这里给出java的规约写法
首先,java的规约由两部分组成

  • 静态类型声明
  • 方法前的注释

方法前的注释由 /** 开头,应包括:

  • 方法的整体描述
  • @param 前置条件,每个参数都要单独写
  • @return 后置条件
  • @throws 可能会抛出的异常

规约的例子:
软件构造第三章学习笔记及心得(二)
值得注意的是,规约中不能包含具体实现,即不要写数据类型。并且在方法中应该尽量避免修改输入参数和使用muttable的对象,除非spec必须如此。这些行为都能在规约中约束住,如规定使用不可变的数据类型。

3.2.2.4 根据规约写测试

  • 黑盒测试
  • 完全按照规约写测试,不考虑具体实现

3.2.3 设计规约

3.2.3.1 规约分类

根据规约的强度来进行划分
一个规约强于另一个规约,表明改规约能够满足另一个规约的实现。
具体的来说,如果规约S2强于规约S1,则S2的前置条件更加弱,后置条件更加强,通俗的讲就是S2对于输入的限制更少,而返回值更加确切。在这张情况下,S2可以代替S1,记作S2>=S1
注1:两个规约之间可能无法比较
注2:越强的规约代表调用者的责任越轻,实现者的责任越重

3.2.3.2 规约图

如果某个实现满足规约,则在图的范围内
规约越强,图的面积越小

3.2.3.3 衡量规约好坏的标准

  • 内聚:spec描述的功能单一,简单,易于理解
  • 信息丰富:看到规约就能够明白方法要干什么
  • 强度足够: client敢用
  • 强度也不能太强:开发者压力不至于过大
  • 尽量在规约中使用抽象类型比如List,不暴露具体实现

是否应该使用前置条件?在方法正式执行前是否要检查前置条件是否满足?
是否使用前置条件取决于(1) check的代价;(2) 方法的使用范围

  • 如果只在类的内部使用该方法(private),那么可以不使用前置条件,在使用该方法的各个位置进行check——责任交给内部client;
  • 如果在其他地方使用该方法(public),那么必须要使用前置条件,若client端不满足则方法抛出异常