规格化设计发展历史
在调研的过程中再一次验证了“百度搜索不适合学术”这个观点,没有找到合适的搜索结果后,转用Google,才找到了一些细节。
保证程序的正确性以及减少软件错误一直是程序员关注的问题,Hoare提出了基于“前置后置条件”的接口规格方法。
为了建立正确的类规格,近些年来,研究者们进行了抽象变量、接口规格、状态抽象、查询方法等不同方面的尝试,并取得了很大的进展。
为保证程序的正确性, Hoare[l] 提出了关于接口的规格方法,即若一种方法在执行前满足前置条件,运行后满足后置条件,那么这种方法就是正确的。 Hoare 提出的这个结论为规格化发展起到了奠基的作用,也为类中接口的规格化提供了理论基础。
针对各方法的精确规格定义, MeyeP] 提出契约式编程延续了对方法的精确定义,但其不足是依赖内部状态来体现方法之间的相互依赖。
课程中使用的JSF更偏向于“契约式设计”(Design by Contract):为传统的抽象数据结构增加了先验条件、后验条件和不变式。
规格bug分析
bug类型 | bug内容 | 出现位置 |
---|---|---|
JSF检查->Requires不完整 | 忘写JSF | Windows类 |
方法规格检查->Effects不完整 | JSF不完整 | Taxi类 |
方法规格检查->JSF不符合规范 | 可追踪出租车没有写继承 | Taxi2类 |
bug产生原因:
- 第九次作业是JSF被扣分最严重的一次。我是写完代码然后再去写JSF,在金工实习上补全JSF就上交了,最后发现因为有一个类的JSF写漏了,对面对每一个没写的方法规格报了15个JSF imcomplete...
- 不是太重视JSF,导致部分后置条件不够详细,最后两次作业总共被报了3个JSF。
前置条件后置条件写法改进
前置条件
(1)修改前
/** @REQUIRES : map!=null,taxiGUI!=null,GuiInfo!=null;
* @MODIFIES : this.id,this.credit,this.map,this.taxiGUI,this.GuiInfo,this.flowMonitor,this.VIP;
* @EFFECTS : None;
*/
对新增变量flowMonitor判断
修改后
/** @REQUIRES : map!=null,taxiGUI!=null,GuiInfo!=null,flowMonitor!=null;
* @MODIFIES : this.id,this.credit,this.map,this.taxiGUI,this.GuiInfo,this.flowMonitor,this.VIP;
* @EFFECTS : None;
*/
(2)修改前
/** @REQUIRES : id > 0;
* @MODIFIES : None;
* @EFFECTS :
esult == id;
*/
不需要对id进行判断
修改后
/** @REQUIRES : None;
* @MODIFIES : None;
* @EFFECTS :
esult == id;
*/
(3)修改前
/** @REQUIRES: None;
* @MODIFIES: None;
* @EFFECTS:
esult == System.currentTimeMills();
*/
对于Thread进行追加Require
修改后
/** @REQUIRES: None;
* @MODIFIES: None;
* @EFFECTS:
esult == System.currentTimeMills();
* @THREAD_REQUIRES:locked( his);
* @THREAD_EFFECTS:locked( his);
*/
(4)修改前
/** @REQUIRES: None;
* @MODIFIES: None;
* @EFFECTS: (distance==1) ==>
esult = true
(distance!=1) ==>
esult = false
*/
对于坐标范围的判断
修改后
/** @REQUIRES: src>0 && src <80 && dst >0 && dst < 80;
* @MODIFIES: None;
* @EFFECTS: (distance==1) ==>
esult = true
(distance!=1) ==>
esult = false
*/
(5)修改前
/** @REQUIRES : s!=null;
* @MODIFIES : this.str;
* @EFFECTS : str == s;
*/
public Request(String s) {
基本数据类型不需要进行判断(编译器保证)
修改后
/** @REQUIRES : None;
* @MODIFIES : this.str;
* @EFFECTS : str == s;
*/
public Request(String s) {
后置条件
(1)修改前
/** @REQUIRES: requestQueue!=null;taxiGUI!=null;
* @MODIFIES: this.requestQueue,this.taxiGUI;
* @EFFECTS: None;
*/
修改后
/** @REQUIRES: requestQueue!=null;taxiGUI!=null;
* @MODIFIES: this.requestQueue,this.taxiGUI;
* @EFFECTS: this.requestQueue == requestQueue, this.taxiGUI == taxiGUI;
*/
(2)修改前
/** @REQUIRES : None;
* @MODIFIES : None;
* @EFFECTS :
esult == invariant(this);
*/
repOK 的具体判断
修改后
/** @REQUIRES : None;
* @MODIFIES : None;
* @EFFECTS :
esult = (requestQueue != null);
*/
(3)修改前
/**@REQUIRES : readFile!=null;taxiGUI!=null;flowMonitor!=null;GuiInfo!=null;
* @MODIFIES : requestQueue,readFile,taxiGUI,flowMonitor,GuiInfo;
* @EFFECTS : this.requestQueue == requestQueue;
* this.readFile == readFile;
* this.taxiGUI == taxiGUI;
* this.flowMonitor == flowMonitor;
*/
对新增的GuiInfo没有修改Effects
修改后
/**@REQUIRES : readFile!=null;taxiGUI!=null;flowMonitor!=null;GuiInfo!=null;
* @MODIFIES : requestQueue,readFile,taxiGUI,flowMonitor,GuiInfo;
* @EFFECTS : this.requestQueue == requestQueue;
* this.readFile == readFile;
* this.taxiGUI == taxiGUI;
* this.flowMonitor == flowMonitor;
* this.GuiInfo == GuiInfo;
*/
(4)修改前
/** @REQUIRES : str!=null;
* @MODIFIES : this.reqSame,this.timeSame;
* @EFFECTS :
equest.equals(同质请求添加到同质判断队列);
*/
尽量转换自然语言为逻辑语句
修改后
/** @REQUIRES : str!=null;
* @MODIFIES : this.reqSame,this.timeSame;
* @EFFECTS : reqSame.add(str),timeSame.add(time)
*/
(5)修改前
/** @REQUIRES : taxis!=null;
* @MODIFIES : None;
* @EFFECTS :None;
*/
get方法一定要写
esult !
修改后
/** @REQUIRES : taxis!=null;
* @MODIFIES : None;
* @EFFECTS :
esult == taxis;
*/
功能bug和规格bug的聚集关系
这次博客作业这样要求,按照可能的论证:由于不清楚代码的功能细节,所以导致出现了规格bug,这种规格bug伴随着方法的功能bug出现,所以会出现聚类关系。
由于JSF是在代码收工之后写的,所以基本没有聚类关系。
三次作业最严重的规格bug就是忘写部分JSF,其他出现规格bug的地方并没有出现功能问题。
如果说有什么没有被报的功能bug,那么对应的规格bug一定是有的,只是没有被抓出来而已。
设计与撰写规格的思路
- Require部分:一般都是对于输入的形参做非空判断
- Modify部分:对于方法写到的private变量挨个判断是否修改
- Effects部分:对于Modify的变量变化的情况进行分析
最后使用JSFTool.class进行复查
心得体会
最近几次OO作业感觉收获比较小,在做课程作业时对于Java的语法没有更深学习的动力,也没有Java与其他技术的结合应用,如果只是完成作业只需要JavaSE的部分语法基础而已。这两周花费大量时间在JSF的书写上,而且遇到对面的刁钻扣分也容易受到打击,可能大家都觉得相对于程序的功能Bug,要扣规格的分不要容易太多吧。
参考文献
[1]王伟,丁二玉,骆斌.基于抽象状态的类的行为规格化方法[J].计算机科学,2016,43(S1):457-460.