软件构造规约设计知识点总结

  已经开始做软件构造实验二了,但做了几天还是感觉不太对,有种无从下手的感觉,我还是来复习一下本次实验所要用到的知识点吧,至于前面的知识呢,先别急,虽然也用的上,但在本次实验不是重点,之后再进行复习。

知识点概要:

  规约设计

  测试用例

  ADT的泛化型

  ADT的抽象函数(AF)、表示不变性(RI)

  OOP实现ADT,并且检测表示泄露(RE)

在本次将进行规约设计的知识点回顾

规约设计

一、规约的简要介绍

  “方法”是程序的“积木”,可以被独立开发、测试、复用使用“方法”的客户端,无需了解方法内部具体如何工作—“抽象”,这是方法的主要思想。而用例规约则是在这一基础上延伸出来的,是一个方法前面的说明性注释,会解释方法的功能、参数、返回值等。

  规约的好处是十分明显的,首先作为开发者,先写下规约可以清晰地明确该方法要实现的功能与限制,并且在之后的开发中不会忘记该方法的作用;而对于客户端,好处则更加明显:无需阅读调用函数的代码,只需理解spec即可,相当便捷高效。

  下图是一个规约的具体例子,首先描述了方法的作用,@param解释了方法中参数的意义及限制,@return解释了方法的返回值。

  一句话概括规约:只讲“能做什么”,不讲“如何实现”!

二、行为等价性

  行为等价性,是用来判断两个函数是否等价(对客户端来说)。

  具体到如何判断两个函数是否行为等价,单纯的看实现代码,并不足以派判定不同的implmentation是否是行为等价的,需要用规约来判断,若两个函数都符合同一个规约,则他们俩行为等价。

三、规范结构:前置条件和后置条件

  前置条件(precondition):对客户端的约束,在使用方法是必须满足的条件,使用关键词requires表明。

  后置条件(postcondition):对开发者的约束,方法结束是必须满足的条件,使用关键词effects表明。

  契约:如果前置条件满足了,后置条件必须满足;前置条件不满足,则方法可做任何事情,“你违约在先,我自然不遵守承诺”。

规约具体设计规则:

  • 参数使用@param描述,结果使用@return、@throws描述
  • 如果可能,将前置条件写入@param中,后置条件写入@return和@throws中
  • 除非在后置条件中声明过,否则方法内部不应该改变输入参数
  • 尽量不设计修改输入参数的规约,减少使用可变对象
  • 描述的功能要单一、简单、易理解

下图是一个具体的规约及函数实现(条件可能出现在@param和@return之外的其他地方,因此一定要仔细读用例规约!!)

 

 

 注意:在规约中一定不能包含方法中的局部变量与私有字段,要使方法中的具体实现对用户invisible。

四、规约的分类

  规约的分类一般分为四种分类方法,分别通过正确性、陈述性和强度进行分类。

按强度进行分类

  用来判断是否可用用一个规约来替换另一个,一般是运用强度对两个规约进行判断。

假如规约强度S2 >= S1,则有:

  • 前置条件S2比S1更弱或相同
  • 后置条件S2比S1更强或相同

较强的规约具有更放松的前置条件 + 更严格的后置条件

下图展示了三个规约强度的比较

 而下图两个规约的强度则无法比较,相较于第一个规约,第二个的前置条件更弱了;但在满足第一个规约的前置条件的情况下,第二个规约相较于第一个其后置条件也弱化了(没有返回最低索引值)。

  总的来说,当规约被增强时:

  • 可满足规约的实现方式更少
  • 更多的用户端可以使用
  • 实现者(开发者)的自由度更小,责任更重
  • 客户端(使用者)责任更轻

 按确定性进行分类  

  确定性规约(Deterministic):给定前置条件,其输出是唯一的、明确的。

  欠定的规约(Underdetermined):同一个输入可以有多个输出,但是一旦使用具体的方法实现了这个规约,那么这个返回值也将会被确定。

  非确定的规约(Not deterministic):同一个输入,多次执行可能得到多个结果,比如涉及到随机数的方法。

按陈述性分类

  操作式规约:有具体的实现细节,如伪代码

  声明式规约:没有内部实现的描述,只有对输入输出的规定

  声明式规约更有价值,更能应对变化。但是操作式规约能够方便开发。

五、规约画图

 

  已该图为例,规约画图有以下几点性质:

  •  某个具体实现,若满足规约,则落在其范围内,否则,在其之外
  • 程序员可以在规约的范围内自由选择实现方式,客户端无需了解具体使用了哪个实现
  • 规约越强,对应的区域越小

  而两个规约无法比较时,则会出现部分重合和不相交的情况,如下图

六、如何设计好的规约

  • 规约描述的功能应该单一、简单、易理解,并且不能产生歧义
  • 规约不应太弱,也不能太强(权衡用户使用与实现难度)
  • 在规约里使用抽象类型,可以给方法的实现体与客户端更大的自由度
  • 是否使用前置条件取决于check的代价和方法的使用范围

七、总结

  • 规约可以减少bug,并使查找bug变得更容易
  • 规约比实现函数本身更易理解,对客户端来说能直接使用方法
  • 规约会使方法代码修改变得简单与安全(只要继续满足契约的要求)

 

posted @ 2023-03-31 20:30  Jayhawk  阅读(103)  评论(0)    收藏  举报