oo第三次总结
-
规格化设计发展
在计算机软件发展的最初阶段,人们对于软件的需求并不算很大。这时候的软件往往逻辑简单,用一些基础的汇编语言就可以轻松的实现所有需求。但是随着计算机行业的发展,计算机处理能力不断提升,软件的复杂度也大幅度提升。为了解决汇编语言编程中遇到的种种不便,人们发明了各式各样的高级语言。但是在这一阶段,受限于存储设备与处理器能力,程序的规模不会很大,更不会出现需要多人协作完成,或者反复修改软件需求的情况。
计算机行业发展的速度超乎人们的想象,人们很快又发现,即使使用了高级语言,当程序规模较大时,代码维护与多人协作仍困难重重。此时,人们开始逐渐注重规格化与标准化编程中的重要性。通过引入这一机制,可以使程序员更好地理解代码的含义,减轻阅读代码的负担,从而高效地完成代码调试、代码调用等基于前人成果进行的工作。
-
规格错误分析
- 第9次作业

因为这一次作业时第一次写jsf,写的时候比较粗心,这些错误都是一些笔误造成的,没有过多的真正实现上的问题。
- 第10次作业

这一次作业就主要是写法问题了,有一处直接写了算法实现,而不是描述的实现结果,而上面的两个不符合规范一个是用自然语言写的过于简洁,缺少了部分内容,一个是在比较字符串相等时写的函数表意不准确,比较的是地址而不是内容。
- 第11次作业
这一次作业同学比较仁慈,再加上有了前两次的教训,写得也比较认真,没有被找bug。不过对方同学也依然提醒我,有一些方法的规格写的不是很完美。
- 规格bug与功能bug关系
从我的作业感觉,规格bug与功能bug没有明显的聚集关系。这一次功能bug主要出现的原因是在程序不断修改需求的过程中,我忽略了一些细节,导致出现了与指导书要求不符的情况。同时,由于修改需求对之前设计有影响(比如加入红绿灯之后最小时间单位从100变为1),我对与这一部分的设计并没有修改完全,导致出现了同质请求无法判断的问题。
- 规格bug原因
由于并不是在程序的一开始就要求写规格,因此所有的规格都是基于第7次作业已有的代码补充的。因此,除了少量的新增代码或者代码修改,不会出现先写规格,后写代码的理想状况。这就导致大部分规格bug都是在后期处理中的不仔细造成的。这也能侧面说明为什么规格bug与功能bug在这几次作业中没有明显联系。
-
规格写法改进
/**
* @REQUIRES: None;
* @MODIFIES: None;
* @EFFECTS: 根据规则判断是否有效;
*/
public boolean repOK() {
if (this.src == null || this.dst == null)
return false;
if (!this.src.repOK() || !this.dst.repOK())
return false;
if (this.time < 0)
return false;
if (this.taxiIndex == null)
return false;
return true;
}
修改effects,之前写的是实现过程
@EFFECTS: \result == invariant(this);
/**
* @REQUIRES: customerReq.matcher(str).matches() == true;
* @MODIFIES: this.server, System.out;
* @EFFECTS: (请求正确) ==> (this.server.add(req));
*/
private void readReq(String str) {
Matcher m = num.matcher(str);
int[] point = new int[4];
for (int i = 0; i < 4; i++) {
m.find();
point[i] = Integer.parseInt(m.group());
if (point[i] >= Main.nodeNum || point[i] < 0) {
System.out.println("#ERROR: input error(req)\n*" + str + "*");
return;
}
}
server.add(new Request(new Node(point[0], point[1]), new Node(point[2], point[3])));
}
修改effects为:
@EFFECTS: (请求正确) ==> (this.server.reqList.reqList.contains(req) && this.server.reqList.size == \old(this.server.reqList.size) + 1)
@REQUIRES: (\exist File f; (f.getName == "output") && (f.isDirectory == true));
这里f.getName == "output" 比较的是两个的地址,而比较是否内容相同应该为如下写法: f.getName.equals("output")
public boolean example(int i) {
try {
if (i == 1)
return true;
else
throws new Exception("not 1");
} catch (Exception e) {
return false;
}
}
一部分同学写effects会这样写
@EFFECTS: normal_behavior (i == 1) ==> (\result == true);
(i != 1) ==> (exception_behavior("not 1") && \result == false);
对于上面这个例子中的方法,在effects中不应当写exception_behavior,因为方法中虽然有抛出异常,但是已经被try catch语句处理了,方法的执行效果中没有抛出异常。上述程序effects的正确写法如下:
@EFFECTS: \result == (i == 1) ? true : false;
-
心得体会
设计规格在会使整体程序变的清晰易懂,能有效的解决程序后期维护的问题。在编写规格中,能使自己专注于对程序功能的理解,而暂时忽略往往显得很繁琐的代码实现,从而更高效的从顶层规划程序结构,完善程序设计思路。规格的书写一定是在确定了需求之后进行,此时头脑中已经有了基本思路,只需按照思路记录即可。
但是在我编写规格的时候发现,有一些方法适合写规格,有一些方法却不适合按照课程组写规格。例如,一些存储信息的类的方法,比如请求类的方法,其功能都非常明确,往往都是获取该请求的一些基本信息。但是一些其他的方法,尤其是各个类中的run方法,却不能像这样很好的归纳,例如负责出租车运行的方法。这类方法的特点就是需要修改大量类的属性,从而完成一些复杂的任务,这些方法无论进行怎么样的拆分,都必然有一部分需要集中的处理复杂逻辑。 而这些复杂逻辑的效果说明,在指导书中花费了大量的篇幅解释,而让我们在编程中去把这一部分说明再次体现,而且限定了语言的表述形式即布尔表达式,就显得有一些困难,有很大几率导致描述不准确。如果对这些方法的规格要求有一些更人性化的处理,就会显得更加合适。

浙公网安备 33010602011771号