$0 写在前面
万里长征已过大半,即将迎来胜利的曙光。一路走来,经历过种种艰难,体会颇深。希望能记录下这篇博文,来总结这一个月来的收获与感悟。
$1 规格化设计的发展历史
上世纪50年代,软件伴随着第一台电子计算机的问世诞生了。以写软件为职业的人也开始出现,他们多是经过训练的数学家和电子工程师。1960年代美国大学里开始出现授予计算机专业的学位,教人们写软件。在计算机系统发展的初期,由于目的的单一性,软件的通用性是很有限的。大多数软件是由使用该软件的个人或机构研制的,软件往往带有强烈的个人色彩。早期的软件开发也没有什么系统的方法可以遵循,软件设计是在某个人的头脑中完成的一个隐藏的过程。而且,除了源代码往往没有软件说明书等文档。
从60年代中期到70年代中期是计算机系统发展的第二个时期,在这一时期软件开始作为一种产品被广泛使用,出现了“软件作坊”专职应别人的需求写软件。这一软件开发的方法基本上仍然沿用早期的个体化软件开发方式,但软件的数量急剧膨胀,软件需求日趋复杂,维护的难度越来越大,开发成本令人吃惊地高,而失败的软件开发项目却屡见不鲜。“软件危机”就这样开始了!“软件危机”使得人们开始对软件及其特性进行更深一步的研究,人们改变了早期对软件的不正确看法。早期那些被认为是优秀的程序常常很难被别人看懂,通篇充满了程序技巧。现在人们普遍认为优秀的程序除了功能正确,性能优良之外,还应该容易看懂、容易使用、容易修改和扩充。
软件工程是一门研究如何用系统化、规范化、数量化等工程原则和方法去进行软件的开发和维护的学科。通过不断努力,人们逐渐解决了软件危机,并认识到规格化设计的重要性,在此期间,一些重要的文档格式的标准被确定下来,包括变量、符号的命名规则以及源代码的规范式。后来随着发展,这些规范逐渐形成了软件开发中的规格化设计,并且由于其高效性与高可靠性,越来越受到软件开发人员的重视。
那么规格化设计的出现,为工程人员带来了哪些方面的便利呢?
伴随着计算机行业的迅速发展,为了解决程序可靠性等问题,结构化、模块化的设计为程序员提供了使用接口。每个模块各司其职,这样的程序便于扩展和维护,大大降低了当时开发者的困难。同时由于面向对象语言兴起,结构化、模块化的思想在面向对象语言里作用被体现得更加彻底。与此同时,为了提高程序语言的规范性,对类和方法进行更好的维护,对其进行规范化设计,也使得数据变得更加安全可控,测试也更加简单易行。
为了类和方法能够变得规范可控,对类和方法进行规范化要求就变得有必要起来。在工程里面, 一个程序也许会有多人同时进行编写,那么由于每个人不同的代码习惯,可能产生不同的设计理念,通过规格化设计也可以同步多人的设计方法,避免产生设计上的漏洞。规格化的程序不仅更易于调试,也更容易被维护,有利于程序的长远生存和发展。这些都是规格化设计的优势所在。
$2 规格bug统计
【表一】被报告的bug概览
功能bug | 规格bug | 是否相关 | |
OO9 | 1 | 1 | 不相关 |
OO10 | 2 | 2 | 不相关 |
OO11 | 2 | 1 | 不相关 |
【注】从表中可以看出功能bug产生的原因与规格无关,故不构成聚集关系。
【表二】被报告bug详细信息
Bug类别 | Bug内容 | 出现位置 |
代码行数 |
功能bug | 未派单给信用最高车辆 | Dispatcher | 23 |
规格bug | 未编写jsf | Taxi.get_stateName() | 15 |
其他 | 说明书不够详细 | ReadMe | 0 |
规格bug | repOK方法不能直接返回(*2) | repOK() | 1 |
规格bug | jsf为表述成javadoc格式 | everywhere | ∞ |
功能bug | 关闭道路无法打开 | gui.initmatrix() | 53 |
【注】由于在第11次作业中遇到了好朋友测试我的作业,该同学前若干次作业被报告了较多bug,出于人道主义援助,我允许该同学申报3个与代码本身无关的bug。即,我并不认为是由于本人代码错误而导致的bug,故在表二中未进行呈现。
$3 规格bug的避免
根据几次作业被报告的规格bug,总结一下避免此类bug的方法:
首先,要认真对待jsf的书写。jsf对我们编写方法起到了规范和约束的重要作用,因此必须对其给出足够的重视,一个端正的态度是写好JSF的第一步。
其次,要仔细复习课件和课本中的内容。通过对样例的研读,将在极大程度上提高我们所编写规格的正确性。
最后,要增加复查环节,当依照相应的jsf编写完方法代码后,需要带入规格进行验证,以保证其正确性。
$4 JSF的修改
以下列举若干在我的JSF存在的问题以及相应的修改策略:
$4-1 Pre-condition 不正确
【No.1】
问题代码段:
public void setMessage(String m) { /** * @REQUIRES:true; * @MODIFIES:message; * @EFFECTS:message != old(message); */ message = m; }
问题:这段代码未对传入参数进行约束,改进如下:
public void setMessage(String m) { /** * @REQUIRES:String m != null; * @MODIFIES:message; * @EFFECTS:message != old(message); */ message = m; }
【No.2】
问题代码段:
protected void waitLight(int n) { /** * @REQUIRES:true; * @MODIFIES:time; * @EFFECTS:(check if taxi has to wait for traffic light) == true; */ while(true) { try { sleep(1); } catch (InterruptedException e) { System.out.println("Exception while waiting for lights"); System.exit(0); } if(n == 0) { // require for NS if(light.getGreenDir() == Dir.NS) { break; } } else if(n == 1) { if(light.getGreenDir() == Dir.EW) { break; } } } }
改进后:
protected void waitLight(int n) { /** * @REQUIRES:all int n : (n == 0 || n == 1); * @MODIFIES:time; * @EFFECTS:(check if taxi has to wait for traffic light) == true; */ while(true) { try { sleep(1); } catch (InterruptedException e) { System.out.println("Exception while waiting for lights"); System.exit(0); } if(n == 0) { // require for NS if(light.getGreenDir() == Dir.NS) { break; } } else if(n == 1) { if(light.getGreenDir() == Dir.EW) { break; } } } }
【No.3】
问题代码段:
protected void checkLight(int n) { /** * @REQUIRES:true; * @MODIFIES:time; * @EFFECTS:(check if taxi has to wait for traffic light) == true; */ if(n == 0) { // going up if(heading == HeadDir.N || heading == HeadDir.E) { if(light.getGreenDir() == Dir.EW) { waitLight(0); } } } else if(n == 1) { // going down if(heading == HeadDir.S || heading == HeadDir.W) { if(light.getGreenDir() == Dir.EW) { waitLight(0); } } } else if(n == 2) { // going left if(heading == HeadDir.N || heading == HeadDir.W) { if(light.getGreenDir() == Dir.NS) { waitLight(1); } } } else if(n == 3) { // going right if(heading == HeadDir.S || heading == HeadDir.E) { if(light.getGreenDir() == Dir.NS) { waitLight(1); } } } }
修改后:
protected void checkLight(int n) { /** * @REQUIRES:all int n : 0 <= n <= 3; * @MODIFIES:time; * @EFFECTS:(check if taxi has to wait for traffic light) == true; */ if(n == 0) { // going up if(heading == HeadDir.N || heading == HeadDir.E) { if(light.getGreenDir() == Dir.EW) { waitLight(0); } } } else if(n == 1) { // going down if(heading == HeadDir.S || heading == HeadDir.W) { if(light.getGreenDir() == Dir.EW) { waitLight(0); } } } else if(n == 2) { // going left if(heading == HeadDir.N || heading == HeadDir.W) { if(light.getGreenDir() == Dir.NS) { waitLight(1); } } } else if(n == 3) { // going right if(heading == HeadDir.S || heading == HeadDir.E) { if(light.getGreenDir() == Dir.NS) { waitLight(1); } } } }
【No.4】
问题代码段:
protected boolean bound(int from_x,int from_y,int to_x,int to_y) { /** * @REQUIRES:true; * @MODIFIES:None; * @EFFECTS:all (int to_x,to_y ==> (0<=to_x<80)) && * (has path between from and to) ==> esult == true; */ if(to_x >= 0 && to_y >= 0 && to_x < 80 && to_y < 80 && graph[from_x * 80 + from_y][to_x * 80 + to_y] == 1) { return true; } else { return false; } }
修改后:
protected boolean bound(int from_x,int from_y,int to_x,int to_y) { /** * @REQUIRES:int from_x,from_y,to_x,to_y;0<=from_x,from_y,to_x,to_y<=80; * @MODIFIES:None; * @EFFECTS:all (int to_x,to_y ==> (0<=to_x<80)) && * (has path between from and to) ==> esult == true; */ if(to_x >= 0 && to_y >= 0 && to_x < 80 && to_y < 80 && graph[from_x * 80 + from_y][to_x * 80 + to_y] == 1) { return true; } else { return false; } }
【No.5】
问题代码段:
public Light(TaxiGUI g) { /** * @REQUIRES:true; * @MODIFIES:gui; * @EFFECTS:this != old (this); */ gui = g; }
修改后:
public Light(TaxiGUI g) { /** * @REQUIRES:g != null; * @MODIFIES:gui; * @EFFECTS:this != old (this); */ gui = g; }
$4-2 Post-condition 不正确
【No.1】
问题代码段:
private static int getDegrees(int i ,int j) { /** * @REQUIRES:true; * @MODIFIES:none; * @EFFECTS: esult = (degrees of this point); */ int cnt = 0; if((i >= 0 && i <= 79 && j >= 0 && j < 79) && guigv.m.graph[i * 80 + j][i * 80 + j + 1] == 1) { cnt ++; } if((i >= 0 && i <= 79 && j > 0 && j <= 79) && guigv.m.graph[i * 80 + j][i * 80 + j - 1] == 1) { cnt ++; } if((i >= 0 && i < 79 && j >= 0 && j <= 79) && guigv.m.graph[i * 80 + j][(i + 1) * 80 + j] == 1) { cnt ++; } if((i > 0 && i <= 79 && j >= 0 && j <= 79) && guigv.m.graph[i * 80 + j][(i - 1) * 80 + j] == 1) { cnt ++; } return cnt; }
@EFFECTS必须是一个boolean型的表达式,故需要使用==来代替=
改进后:
private static int getDegrees(int i ,int j) { /** * @REQUIRES:true; * @MODIFIES:none; * @EFFECTS: esult == (degrees of this point); */ int cnt = 0; if((i >= 0 && i <= 79 && j >= 0 && j < 79) && guigv.m.graph[i * 80 + j][i * 80 + j + 1] == 1) { cnt ++; } if((i >= 0 && i <= 79 && j > 0 && j <= 79) && guigv.m.graph[i * 80 + j][i * 80 + j - 1] == 1) { cnt ++; } if((i >= 0 && i < 79 && j >= 0 && j <= 79) && guigv.m.graph[i * 80 + j][(i + 1) * 80 + j] == 1) { cnt ++; } if((i > 0 && i <= 79 && j >= 0 && j <= 79) && guigv.m.graph[i * 80 + j][(i - 1) * 80 + j] == 1) { cnt ++; } return cnt; }
【No.2】
问题代码段:
private void timePass() { /** * @REQUIRES:true; * @MODIFIES:time; * @EFFECTS:time != old(time); */ try { sleep(cycle); } catch (InterruptedException e) { System.out.println("Exception in light thread!"); System.exit(0); } }
@EFFECTS约束不够具体需要给出较强的约束
改进后:
private void timePass() { /** * @REQUIRES:true; * @MODIFIES:time; * @EFFECTS:time != old(time) + cycle; */ try { sleep(cycle); } catch (InterruptedException e) { System.out.println("Exception in light thread!"); System.exit(0); } }
【No.3】
问题代码段:
private void initialize() { /** * @REQUIRES:true; * @MODIFIES:cycle,green,lightMap,gui; * @EFFECTS:(Initialize the lights in the map) == true; */ cycle = (int) ((Math.random() * 500) + 500); int r = (int) (Math.random()); if(r >= 0.5) { green = Dir.NS; for(int i = 0 ; i < 80 ; i ++) { for(int j = 0 ; j < 80 ; j ++) { if(lightMap[i][j] == 1) { gui.SetLightStatus(new Point(i,j), 2); } } } } else { green = Dir.EW; for(int i = 0 ; i < 80 ; i ++) { for(int j = 0 ; j < 80 ; j ++) { if(lightMap[i][j] == 1) { gui.SetLightStatus(new Point(i,j), 1); } } } } }
尽量减少自然语言的使用
改进后:
private void initialize() { /** * @REQUIRES:true; * @MODIFIES:cycle,green,lightMap,gui; * @EFFECTS:lightMap != old(lightMap); */ cycle = (int) ((Math.random() * 500) + 500); int r = (int) (Math.random()); if(r >= 0.5) { green = Dir.NS; for(int i = 0 ; i < 80 ; i ++) { for(int j = 0 ; j < 80 ; j ++) { if(lightMap[i][j] == 1) { gui.SetLightStatus(new Point(i,j), 2); } } } } else { green = Dir.EW; for(int i = 0 ; i < 80 ; i ++) { for(int j = 0 ; j < 80 ; j ++) { if(lightMap[i][j] == 1) { gui.SetLightStatus(new Point(i,j), 1); } } } } }
【No.4】
问题代码段:
protected void initialize() { /** * @REQUIRES:true; * @MODIFIES:graph,gui; * @EFFECTS:graph and gui has been initialized; */ graph = gui.get_graph(); gui.SetTaxiStatus(taxiNum, new Point(curLocation.getX(),curLocation.getY()) , 2); }
需要减少自然语言的使用
改进后:
protected void initialize() { /** * @REQUIRES:true; * @MODIFIES:graph,gui; * @EFFECTS:this.gui !=old(this.gui); */ graph = gui.get_graph(); gui.SetTaxiStatus(taxiNum, new Point(curLocation.getX(),curLocation.getY()) , 2); }
【No.5】
问题代码段:
protected void serve(){ /** * @REQUIRES:true; * @MODIFIES:normal taxi state; * @EFFECTS:run on taxis service; */ src = new Coordinate(req.getSrc_x(),req.getSrc_y()); dst = new Coordinate(req.getDst_x(),req.getDst_y()); gui.SetTaxiStatus(taxiNum, new Point(curLocation.getX(),curLocation.getY()) , 3); boolean fetch = true; // from current spot to src spot boolean arrived = false; while(!arrived) { stay(500); if(fetch) { fetch = toSrc(); } else { arrived = toDst(); } } }
应减少自然语言的使用。
改进后:
protected void serve(){ /** * @REQUIRES:true; * @MODIFIES:normal taxi state; * @EFFECTS:for normal taxis: taxi.state != old(taxi.state); */ src = new Coordinate(req.getSrc_x(),req.getSrc_y()); dst = new Coordinate(req.getDst_x(),req.getDst_y()); gui.SetTaxiStatus(taxiNum, new Point(curLocation.getX(),curLocation.getY()) , 3); boolean fetch = true; // from current spot to src spot boolean arrived = false; while(!arrived) { stay(500); if(fetch) { fetch = toSrc(); } else { arrived = toDst(); } } }
$5 写在最后
又一阶段的OO任务告一段落,在最后,为这段时间的努力做一个简单的总结,也记录一下几次作业的所见所感。
对于这门课程,无论是从前人抑或是同学那里所得到的反馈来看,负面评论较多。平心而论,在这门课程中,我确实学习到了一些面向对象编程的基本技巧,当然要有更深一步的了解,还有赖于以后更多的训练。吴际等老师为了这门课可以说花费了很多心思,尽管确实会有同学为了获取分数,而利用机制的漏洞,但我仍然认为掌握我们所必须学会的技能才是这门课程的核心所在。同时,吴际老师的严格要求也让我彻底放弃了对分数的追求。上一次的上机实验课的时候我未能按时出席,因为室友胸部意外受伤,我陪同前往医院检查。事出突发,未能及时向学院递交请假申请,但吴际老师拒绝接受任何补交假条。于是现在关于这门课程最后我能否通过,仍然是个未知数。虽然吴际老师对我们的要求过于严苛,但我也仍然十分感激,哪怕每次训练只有一点点收获,日积月累,我相信便会有所突破。
看淡分数,提升能力,享受编码。