面向对象第三次博客总结
一、规格的历史和重要性
早期的软件开发也没有什么系统的方法可以遵循,软件设计是在某个人的头脑中完成的一个隐藏的过程。而且,除了源代码往往没有软件说明书等文档。
从60年代后,软件开始作为一种产品被广泛使用。这一软件开发的方法基本上仍然沿用早期的个体化软件开发方式,但软件的数量急剧膨胀,软件需求日趋复杂,维护的难度越来越大,开发成本令人吃惊地高,而失败的软件开发项目却屡见不鲜。“软件危机”就这样开始了!人们改变了早期对软件的不正确看法。早期那些被认为是优秀的程序常常很难被别人看懂,通篇充满了程序技巧。现在人们普遍认为优秀的程序除了功能正确,性能优良之外,还应该容易看懂、容易使用、容易修改和扩充。
为了类和方法能够变得规范可控,对类和方法进行规范化要求就变得有必要起来。在工程里面, 一个程序也许会有多人同时进行编写,那么由于每个人不同的代码习惯,可能产生不同的设计理念,通过规格化设计也可以同步多人的设计方法,避免产生设计上的漏洞。规格化的程序不仅更易于调试,也更容易被维护,有利于程序的长远生存和发展。这些都是规格化设计的优势所在。
二、规格bug
作业次数 | 规格BUG类型 | 出现方法行数 | 出现原因 |
第九次 | 无 | ||
第十次 | Main.java没写rep
OK()函数 |
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; } //构造操作
四、列举不好的写法并改进
1.往txt写入时要确保出租车对象有意义
public void write() { /** @MODIFIES:this.file; * @EFFECTS: */ } //修改后 public void write() { /** @Requires:this.reoOK() == true * @MODIFIES:this.file; * @EFFECTS: */ }
2.确保传入参数有坐标与方向意义
public boolean can_go(int x,int y,int dir) { /** @MODIFIES: NONE; * @EFFECTS: } //修改 public boolean can_go(int x,int y,int dir) { /** @Requires:(0<=x<=79)&&(0<=y<=79)&&(0<=dir<=3); * @MODIFIES: NONE; * @EFFECTS: }
3.确保time是有意义的
public int waittime() { /** * @MODIFIES: NONE; * @EFFECTS: * \result == (700 - (System.currentTimeMillis() - time)) */ } //修改后 public int waittime() { /** * @Requires: this.time > 0 * @MODIFIES: NONE; * @EFFECTS: * \result == (700 - (System.currentTimeMillis() - time)) */ }
4.确保有计算距离的地图
public int getdis(Point a, Point b) { /**@MODIFIES:NONE; * @EFFECTS: * /result == dist from a to b; */ } //改后 public int getdis(Point a, Point b) { /**Requires:repOK()==ture; * @MODIFIES:NONE; * @EFFECTS: * /result == dist from a to b; */ }
5.REQUIRES的判断式分开更能看清逻辑
public void getroute(int x1,int y1,int x2,int y2,int [][]route) { /** * @REQUIRES: (0<=x1<=79 && 0<=y1<=79 && 0<=x2<=79 && 0<=y2<=79); * @MODIFIES:route; * @EFFECTS: * route == routine form x1 y1 to x2 y2; */ } //改后 public void getroute(int x1,int y1,int x2,int y2,int [][]route) { /** * @REQUIRES: (0<=x1<=79 )&& (0<=y1<=79) && (0<=x2<=79) && (0<=y2<=79); * @MODIFIES:route; * @EFFECTS: * route == routine form x1 y1 to x2 y2; */
后置条件:
6.避免自然语言描写
public void run() { /** * @MODIFIES:this; * @EFFECTS: * run a circle */ } //改后 public void run() { /** * @MODIFIES:this; * @EFFECTS: * \old(this.past_time) < 7500 ==> this.past_time == \old(this.past_time) + 500 ; * @THREAD_EFFECTS: \locked(clock); */ }
7.自增需要用到\old()
public void run() { /** * @MODIFIES: road.count,x,y,ox,oy,status,wait,runtime; * @EFFECTS: *run this cab a cycle; *runtime == \old(runtime) + 500; *road.count == road.count + 1; *(road.count == 100) ==> ((road.count == 0) && ((\all int i,i;0=<i,j<=80) ==>(flow[i][j][0]==\old(flow[i][j][2])&&flow[i][j][1]==\old(flow[i][j][3])&&flow[i][j][2]==0&&flow[i][j][3]==0))); * @THREAD_EFFECTS: \locked(clock); */ } //改后 public void run() { /** * @MODIFIES: road.count,x,y,ox,oy,status,wait,runtime; * @EFFECTS: *run this cab a cycle; *runtime == \old(runtime) + 500; *road.count == \old(road.count) + 1; *(road.count == 100) ==> ((road.count == 0) && ((\all int i,i;0=<i,j<=80) ==>(flow[i][j][0]==\old(flow[i][j][2])&&flow[i][j][1]==\old(flow[i][j][3])&&flow[i][j][2]==0&&flow[i][j][3]==0))); * @THREAD_EFFECTS: \locked(clock); */
8.
public void getbill(Request r) { /** * @MODIFIES: this; * @EFFECTS: *this.carry_re == r; *this.ox ==r.set_x && this.oy == r.set_y; *this.status == 1 && this.runtime == 0 && this.wait == 0; *this.headpoint == new Point; */ } //改后 public void getbill(Request r) { /** * @MODIFIES: this; * @EFFECTS: *this.carry_re == r; *this.ox ==r.set_x && this.oy == r.set_y; *this.status == 1 && this.runtime == 0 && this.wait == 0; *this.headpoint == next_head_point; */ }
9.
public void run() { /** * @MODIFIES: this.lights; * @EFFECTS: * (\all int i,j;0<=i,j<=79;\old(this.lights[i][j])!=0) ==> (\old(lights[i][j]) == 1 ==> lights[i][j] == 2) && (\old(lights[i][j]) == 2 ==> lights[i][j] == 1); */ } //改后 public void run() { /** * @MODIFIES: this.lights; * @EFFECTS: * (\all int i,j;0<=i,j<=79;\old(this.lights)[i][j]!=0) ==> (\old(lights)[i][j]== 1 ==> lights[i][j] == 2) && (\old(lights)[i][j] == 2 ==> lights[i][j] == 1); */
10.
public void run_1() { /** * @MODIFIES: this; * @EFFECTS: *(\old(this.wait) == 0) ==> ((this.x == this.ox && this.y == this.oy)==> (this.wait == 1 && this.runtime == 0)); *(\old(this.wait) == 0) ==> (!(this.x == this.ox && this.y == this.oy)==>(this.x = next_x&& this.y = next_y)); *(\old(this.wait) == 1) ==> (carry_re.past_time == \old(carry_re.past_time)+500 && (carry_re.past_time == 1000 ==> (wait == 2 && this.runtime == 0))); *(\old(this.wait) == 2) ==> ((this.x == this.ox && this.y == this.oy)==> (this.wait == 0 && this.runtime == 0 && this.status == 0)); *(\old(this.wait) == 2) ==> (!(this.x == this.ox && this.y == this.oy)==>(this.x = next_x&& this.y = next_y)); */ } //改后 public void run_1() { /** * @MODIFIES: this; * @EFFECTS: *(\old(this.wait) == 0) ==> ((this.x == this.ox && this.y == this.oy)==> (this.wait == 1 && this.runtime == 0)); *(\old(this.wait) == 0) ==> (!(this.x == this.ox && this.y == this.oy)==>(this.x = next_x&& this.y = next_y)); *(\old(this.wait) == 1) ==> (carry_re.past_time == \old(carry_re.past_time)+500 && (carry_re.past_time == 1000 ==> (wait == 2 && this.runtime == 0))); *(\old(this.wait) == 2) ==> ((this.x == this.ox && this.y == this.oy)==> (this.wait == 0 && this.runtime == 0 && this.status == 0)); *(\old(this.wait) == 2) ==> (!(this.x == this.ox && this.y == this.oy)==>(this.x = next_x&& this.y = next_y)); *this.detai update */ }
五、功能BUG与规格BUG的内聚关系
作业 | 规格Bug数 | 功能Bug数 | 内聚关系 |
第九次 | 0 | 0 | 无 |
第十次 | 5 | 4 | 无 |
第十一次 | 0 | 3 | 无 |
六、感想体会
这三次作业很多函数都是以前写好的,只能补充规格。而新的方法,我习惯先写方法体再写规格,因为在整个程序完成之前,我不确定一个函数是否还有需要再次进行修改的地方,先写规格明显不符合习惯,即使规格有着导向性作业,但并不具有细节处调节的能力。