软件构造第三章学习笔记及心得(二)
软件构造这门课是比较具有挑战性,知识点多且琐碎的,因此,我将结合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端不满足则方法抛出异常