$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) ==> \result == 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) ==> \result == 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:\result = (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:\result == (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任务告一段落,在最后,为这段时间的努力做一个简单的总结,也记录一下几次作业的所见所感。

对于这门课程,无论是从前人抑或是同学那里所得到的反馈来看,负面评论较多。平心而论,在这门课程中,我确实学习到了一些面向对象编程的基本技巧,当然要有更深一步的了解,还有赖于以后更多的训练。吴际等老师为了这门课可以说花费了很多心思,尽管确实会有同学为了获取分数,而利用机制的漏洞,但我仍然认为掌握我们所必须学会的技能才是这门课程的核心所在。同时,吴际老师的严格要求也让我彻底放弃了对分数的追求。上一次的上机实验课的时候我未能按时出席,因为室友胸部意外受伤,我陪同前往医院检查。事出突发,未能及时向学院递交请假申请,但吴际老师拒绝接受任何补交假条。于是现在关于这门课程最后我能否通过,仍然是个未知数。虽然吴际老师对我们的要求过于严苛,但我也仍然十分感激,哪怕每次训练只有一点点收获,日积月累,我相信便会有所突破。

看淡分数,提升能力,享受编码。

OO第三阶段纪实的更多相关文章

  1. OO第三阶段作业总结

    调研:        最早的程序设计是直接采用机器语言来编写的,或者使用二进制码来表示机器能够识别和执行的指令和数据.机器语言的优点在于速度快,缺点在于写起来实在是太困难了,编程效率低,可读性差,并且 ...

  2. OO第三阶段总结

    软件形式化方法历史 形式化方法的研究高潮始于20世纪60年代后期,针对当时所谓"软件危机",人们提出种种解决方法,归纳起来有两类:一是采用工程方法来组织.管理软件的开发过程:二是深 ...

  3. 2021S软件工程——结对项目第三阶段

    2021S软件工程--结对项目第三阶段 2021春季软件工程(罗杰 任健) 项目地址 1020 1169 1 实践反思 1.1 问题分析 两人习惯不一致 没有具体制定时间节点 写完代码才开始" ...

  4. OO前三次作业思考(第一次OO——Blog)

    OO前三次作业总结 基于度量分析程序结构 由于三次作业较多,决定分析内容.功能最为复杂的第三次作业. 上图为第三次作业的类图.我使用了一个抽象类Factor,写了五个因子继承Factor,然后又单独开 ...

  5. Bete冲刺第三阶段

    Bete冲刺第三阶段 今日工作: web: 检索了各类资料,今日暂时顺利解决了hibernate懒加载异常的问题,采用的凡是也比较简单就是添加了一个OpenSessionInViewFilter的过滤 ...

  6. [课程设计]Scrum 3.1 多鱼点餐系统开发进度(第三阶段项目构思与任务规划)

    Scrum 3.1 多鱼点餐系统开发进度(第三阶段项目构思与任务规划) 1.团队名称:重案组 2.团队目标:长期经营,积累客户充分准备,伺机而行 3.团队口号:矢志不渝,追求完美 4.团队选题:餐厅到 ...

  7. Scrum 3.1 多鱼点餐系统开发进度(第三阶段项目构思与任务规划)

    Scrum 3.1 多鱼点餐系统开发进度(第三阶段项目构思与任务规划) 1.团队名称:重案组 2.团队目标:长期经营,积累客户充分准备,伺机而行 3.团队口号:矢志不渝,追求完美 4.团队选题:餐厅到 ...

  8. 分布式事务 & 两阶段提交 & 三阶段提交

    可以参考这篇文章: http://blog.csdn.net/whycold/article/details/47702133 两阶段提交保证了分布式事务的原子性,这些子事务要么都做,要么都不做. 而 ...

  9. 第一阶段,第二阶段,第三阶段团队github更新项目地址

    第一阶段:https://github.com/yuhancheng/stage-1--last-sprint 第二阶段:https://github.com/yuhancheng/stage-2-- ...

随机推荐

  1. Chrome浏览器的版本查看 以及V8 javascript 引擎版本查看

    1. 发现chrome浏览器最新版本里面带的V8 引擎 版本号与chrome的版本号有一个关系, 这里简单总结一下: 在地址栏里面输入: chrome://version 即可显示出来 比如我正在使用 ...

  2. C# Note26: [MethodImpl(MethodImplOptions.Synchronized)]与lock机制

    在进行.NET开发时,经常会遇见如何保持线程同步的情况.在众多的线程同步的可选方式中,加锁无疑是最为常用的.如果仅仅是基于方法级别的线程同步,使用System.Runtime.CompilerServ ...

  3. CentOS7装Tomcat

    有两种安装方式:(1)yum 命令  (2)安装包 本次采用第二种方式: 1.windos下载apache-tomcat-7.0.73.tar.gz安装包 2.通过WinSCP传到linux下(本次放 ...

  4. StringBuilder与String有哪些区别?

    System.String具备不可修改性,在程序中这样的特性容易产生性能上的问题.针对这个问题.NET提供的StringBuilder类可以解决类似的问题. String 和 StringBuilde ...

  5. Python:matplotlib绘制散点图

    与线型图类似的是,散点图也是一个个点集构成的.但不同之处在于,散点图的各点之间不会按照前后关系以线条连接起来. 用plt.plot画散点图     奇怪,代码和前面的例子差不多,为什么这里显示的却是散 ...

  6. DAY05、基本数据类型与内置方法

    一.可变类型与不可变类型: 1.可变类型:值改变,但是id不变 2.不可变类型:值改变,id也改变 二.数据类型: 1.数字类型: 1.1:整型int: 用途:记录年龄.等级.数量 定义方式:age ...

  7. 初识Anrdiod SDK

    概念 SDK:(software development kit)软件开发工具包.被软件开发工程师用于为特定的软件包.软件框架.硬件平台.操作系统等建立应用软件的开发工具的集合. 因此,Android ...

  8. Mybatis之执行自定义SQL举例

    本文说明如何使用Mybatis执行我自定义输入的SQL语句. 需要的mybaits文件包括:配置文件(mybatis-config-dao.xml 和 jdbc.properties).接口文件(IS ...

  9. LodopJS文档式模版的加载和赋值

    Lodop模版有两种方法,一种是传统的JS语句,可以用JS方法里的eval来执行,一种是文档式模版,是特殊格式的base64码,此篇博文介绍文档式模版的加载,文档式模版的生成以及传统JS模版的生成加载 ...

  10. ES 6 系列 - Module 的语法

    es 6 大幅度优化了模块化编程的规范. 写在前面:在 es6 之前,说起 js 的模块化,一般都避不开 CommonJs 和 AMD 两种方案.这两种方案,前者应用于服务器,后者应用于浏览器.而 e ...