一、规格的历史和重要性
早期的软件开发也没有什么系统的方法可以遵循,软件设计是在某个人的头脑中完成的一个隐藏的过程。而且,除了源代码往往没有软件说明书等文档。
从60年代后,软件开始作为一种产品被广泛使用。这一软件开发的方法基本上仍然沿用早期的个体化软件开发方式,但软件的数量急剧膨胀,软件需求日趋复杂,维护的难度越来越大,开发成本令人吃惊地高,而失败的软件开发项目却屡见不鲜。“软件危机”就这样开始了!人们改变了早期对软件的不正确看法。早期那些被认为是优秀的程序常常很难被别人看懂,通篇充满了程序技巧。现在人们普遍认为优秀的程序除了功能正确,性能优良之外,还应该容易看懂、容易使用、容易修改和扩充。
为了类和方法能够变得规范可控,对类和方法进行规范化要求就变得有必要起来。在工程里面, 一个程序也许会有多人同时进行编写,那么由于每个人不同的代码习惯,可能产生不同的设计理念,通过规格化设计也可以同步多人的设计方法,避免产生设计上的漏洞。规格化的程序不仅更易于调试,也更容易被维护,有利于程序的长远生存和发展。这些都是规格化设计的优势所在。
二、规格bug
作业次数 | 规格BUG类型 | 出现方法行数 | 出现原因 |
第九次 | 无 | ||
第十次 | Main.java没写rep
|
1 | 所有的类,只要有变量,且可能发生变化就要有repOK,但我却忽略main |
Requires不完整 | 1 | 对Requires不甚理解 | |
Main.java没写Overview | 1 | main总是一个容易让人遗忘的特殊类 | |
Effects不完整 | 7 | 这七处错误在于7个写入txt文件的函数,因为不产生effect,下意识地忽视了,因该填上NONE | |
不变式的判断缺少与repOK()疏漏 | 1 | 有些属性缺失很难通过不变式确立 | |
第十一次 | 无 |
三、规格改进
1.Main的repOK()函数补充
public boolean repOK() { /** * @Effects: \result==invariant(this) */ int i ,j; for(i=0;i<80;i++) { for(j=0;j<80;j++) { if(map[i][j]<0||map[i][j]>3) return false; if(flow[i][j][0]<0) return false; if(flow[i][j][1]<0) return false; if(flow[i][j][2]<0) return false; if(flow[i][j][0]<0) return false; } } if(gui == none) return false; if(clk == none) return false; return true; }
2.Requires补充
//改前 public int waittime() { /** * @MODIFIES: NONE; * @EFFECTS: * \result == (700 - (System.currentTimeMillis() - time)) */ long t1; t1 = System.currentTimeMillis(); //if(t1%100 >=50) t2 = t1 - t1%100 + 100; //else t2 = t1 - t1%100; return 700 - (int)(t1 - time)%700; } //改后 public int waittime() { /** * @Requires: this.time > 0 * @MODIFIES: NONE; * @EFFECTS: * \result == (700 - (System.currentTimeMillis() - time)) */ long t1; t1 = System.currentTimeMillis(); //if(t1%100 >=50) t2 = t1 - t1%100 + 100; //else t2 = t1 - t1%100; return 700 - (int)(t1 - time)%700; }
3.Main补充Overview
/** * @Overview: Main is the main function. **/
4.Effects补充
//改前 /** * @MODIFIES:this.file; * @EFFECTS: */ //改后 /** * @MODIFIES:this.file; * @EFFECTS:write details in txt */ //或 /** * @MODIFIES:this.file; * @EFFECTS:NONE */
5.不变式与repOK()的补充
/** * @表示对象:Request []request,Object clock,Texi[]texi,Route road,int num,int head * @抽象函数:AF(c) = (request_list,clock,texi_list,road,last_request_position)where *c.request == request_list,c.clock == clock,c.texi==texi_list,c.road==road,c.num==last_request_position,c.head == 0 * @invariant:(\all int i;0<=i<=num;request[i]!=null&&request[i].repOK())&&(clock!=null)&&(\all int i;0<=i<=99;texi[i]!=null&&texi[i].repOK())&& * (road!=null&&road.repOK()) **/ public boolean repOK() { /** * @Effects: \result==invariant(this) */ int i; if(clock==null) return false; for(i=0;i<nums;i++) { if(request[i]==null||!request[i].repOK()) return false; } for(i=0;i<100;i++) { if(texi[i]==null||!texi[i].repOK()) return false; } if(road==null||!road.repOK()) return false; return true; } //改后 /** * @表示对象:Request []request,Object clock,Texi[]texi,Route road,int num,int head * @抽象函数:AF(c) = (request_list,clock,texi_list,road,last_request_position)where *c.request == request_list,c.clock == clock,c.texi==texi_list,c.road==road,c.num==last_request_position,c.head == 0 * @invariant:(\all int i;0<=i<=num;request[i]!=null&&request[i].repOK())&&(clock!=null)&&(\all int i;0<=i<=99;texi[i]!=null&&texi[i].repOK())&& * (road!=null&&road.repOK())&&(taxigui !=null) **/ public boolean repOK() { /** * @Effects: \result==invariant(this) */ int i; if(clock==null) return false; for(i=0;i<nums;i++) { if(request[i]==null||!request[i].repOK()) return false; } for(i=0;i<100;i++) { if(texi[i]==null||!texi[i].repOK()) return false; } if(road==null||!road.repOK()) return false; if(taxigui == null) return false; return true; } //构造操作
四、功能BUG与规格BUG的内聚关系
作业 | 规格Bug数 | 功能Bug数 | 内聚关系 |
第九次 | 0 | 0 | 无 |
第十次 | 5 | 4 | 无 |
第十一次 | 0 | 3 | 无 |
五、感想体会
这三次作业很多函数都是以前写好的,只能补充规格。而新的方法,我习惯先写方法体再写规格,因为在整个程序完成之前,我不确定一个函数是否还有需要再次进行修改的地方,先写规格明显不符合习惯,即使规格有着导向性作业,但并不具有细节处调节的能力。
此外,互测阶段很容易爆发矛盾,据我所知,因为此而骂起来的不在少数,我很能理解一个人的心血之作被别人弄的遍体鳞伤的