OO作业总结(三)
类规格设计
由于没能找到关于类规格设计的发展历史,所以结合程序设计思想的发展来谈谈规格化设计。
最早的程序设计都是采用机器语言来编写的,直接使用二进制码来表示机器能够识别和执行的指令和数 据。简单来说,就是直接编写 0 和 1 的序列来代表程序语言。机器语言由机器直接执行,速度快,但一个很明显的缺点就是:写起来实在是太困难了;于是有了面向过程变成,相比面向机器的思想来说,面向过程是一次思想上的飞跃,将程序员从复杂的机器操作和运行的细节中解放出来,转而关注具体需要解决的问题;面向过程的语言也不再需要和具体的机器绑定,从而具备了移植 性和通用性;面向过程的语言本身也更加容易编写和维护。这些因素叠加起来,大大减轻了程序员的负担, 提升了程序员的工作效率,从而促进了软件行业的快速发展。
但在这种设计模式下,不可避免的会产生面条式的代码,极大地限制了程序的规模——结构化设计应运而生:它是一种编程范型,采用子程序(函数就是一种子程序)、代码区块、for循环以及while循环等结构,来替换传统的goto。希望借此来改善计算机程序的明晰性、质量以及开发时间,并且避免写出面条式代码,这就是规格化设计的起源,并且一直发展至今。
在实际的生活中,开发都是以团队为单位的,团队之间的交流主要是通过代码,所以代码要具有可读性;同时,实际的生活中代码的维护也是相当重要的,而维护代码的那些人却并不一定是代码的作者,这就更要求代码要具有很好的可维护性;另外,在我们开发程序的过程中,规格的设计更有助于避免浪费更多的时间来debug,如果一开始设计好了规格,然后严格按照这个规格来完成代码,可以很好的避免功能性bug。因此,由于以上列举出来的部分优点,这些看似优点浪费时间的类规格一直在人们心中占据重要的位置。
bug分析
本次课程如果烧脑的地方除了互测估计就是这个jsf的书写了,并不是说难度很大,而是要写得真正的规范感觉太难,而且很耗时间,写了这么多规格并没有很大的收获。所以这几次的规格都是写完了方法然后后面来添加的,并没有达到规格应有的左右,比如实验课上先写规格然后再写方法代码。这几次作业测我程序的同学都很nice,并没有扣我规格的bug。
关于功能性的bug,由于这几次作业是在第七次作业的基础上进行修改,所以只要实现了指导书的要求,应该能很好的避免功能性bug。第十一次作业被同学报了一个功能bug,是关于走回头路的。我自己本地跑了一下他的测试代码(所有的车初始化在一个点,而且该点周围只有一条边),现象是根本看不出现象,可能是因为电脑太卡了吧也可能是程序本身就有问题,不过我依然觉得这种测试样例可能大部分代码跑出来效果都一样,所以自己也没去管这个bug了。
不好的写法和改进
(1)以下是我出租车类中设置状态的方法:
/**@REQUIRE: None; * @MODIFIES: status; * @EFFECTS: * (s == 3) ==> status == Status.STILL; * (s == 0) ==> status == Status.SERVE; * (s == 2) ==> status == Status.WAIT; * (s == 1) ==> status == Status.GOTORDER; * @ THREAD_EFFECTS:\locked(); */
以上的规格感觉应该是合理的,就是觉得有点丑,所以稍微改了一下(可能还是很丑):
/**@REQUIRE: None; * @MODIFIES: status; * @EFFECTS: (s == 3) ==> status == Status.STILL || (s == 0) ==> status == Status.SERVE || (s == 2) ==> status == Status.WAIT || (s == 1) ==> status == Status.GOTORDER; * @ THREAD_EFFECTS:\locked(); */
(2)以下是我出租车类中的设置目标点的规格:
/** * @ MODIFIES: to; * @ EFFECTS: * this.to == to; * @ THREAD_EFFECTS:\locked(); */
而这个方法的实现是这样的:
public synchronized void setTo(Point p) { to.setLocation(p.x, p.y); }
很明显这个规格的REQUIRE是要写的,所以改进如下:
/**@ REQUIRES: p != null; * @ MODIFIES: to; * @ EFFECTS: * this.to == to; * @ THREAD_EFFECTS:\locked(); */
(3)以下是我周期为7.5s的监控线程的构造方法 ,同理其REQUIRE也有问题:
/** @ MODIFIES: TIME; taxi; from; to; num; @ EFFECTS: this.TIME == TIME; this.taxi == taxi; this.num == num; this.from == from; this.to == to; */
改进:
/**@REQUIRE: taxi != null; from != null; to != null; TIME >= 0; @ MODIFIES: TIME; taxi; from; to; num; @ EFFECTS: this.TIME == TIME; this.taxi == taxi; this.num == num; this.from == from; this.to == to; */
(4)接下来是我调度类中的一个获取出租车信息的方法的规格:
/**@ REQUIRES: str != {}; @ MODIFIES: None; @ EFFECTS: (\exist int i; 0 <= i < str.length; str.charAt(i) >= '0' && str.charAt(i) <= '9') && (\all int j; 0 <= j < i; str.charAt(j) < '0' || str.charAt(j) > '9') && (\exist int k; i <= k < str.length; str.charAt(k) >= '0' && str.charAt(k) <= '9') && (k+1 >= str.length || str.charAt(k+1) < '0' || str.charAt(k+1) > '9') && (\all int l; i <= l <= k; str.charAt(l) >= '0' && str.charAt(l) <= '9') ==> (\result == new Point(Integer.parseInt(str.substring(i, k+1), k+1))); */
乍一看感觉很合理,但是仔细看却发现有错误:比如第一个逻辑表达式中定义的i的作用域只有第一行代码,并不能被后面的代码所识别,改进如下:
/**@ REQUIRES: str != {}; @ MODIFIES: None; @ EFFECTS: (\exist int i; 0 <= i < str.length; (str.charAt(i) >= '0' && str.charAt(i) <= '9' && (\all int j; 0 <= j < i; str.charAt(j) < '0' || str.charAt(j) > '9') && (\exist int k; i <= k < str.length; str.charAt(k) >= '0' && str.charAt(k) <= '9') && (k+1 >= str.length || str.charAt(k+1) < '0' || str.charAt(k+1) > '9') && (\all int l; i <= l <= k; str.charAt(l) >= '0' && str.charAt(l) <= '9')) ==> (\result == new Point(Integer.parseInt(str.substring(i, k+1), k+1))); */
改进之后就可以改变上面的不足。
(5)接下来是我十一次作业的特殊出租车的构造方法:
/**@REQUIRES: * No >= 0 && No < 100; mi != null; taxigui != null; * @MODIFIES: * No; mi; taxigui; * @EFFECTS: * this.mi == mi; this.taxigui == taxigui; this.No == No; */
这个规格和第四个规格类似,都感觉是合理的,却有一点点小问题,传进来的taxigui不能保证是合法的,所以REQUIRE得重写,改进如下:
/**@REQUIRES: * No >= 0 && No < 100 && mi != null && taxigui != null && taxigui.repOk(); * @MODIFIES: * No; mi; taxigui; * @EFFECTS: * this.mi == mi; this.taxigui == taxigui; this.No == No; */
基本思路和体会
这几次作业的规格都是在完成代码之后添加的,所以不一定合乎要求,不过在实验课上尝试了先写规格再写代码,觉得这样的模式也还行,不过这就对设计的要求高了一些,如果一开始设计有误,会浪费更多时间。
最后一次oo的代码作业,好像并没有什么特别的感觉,以后终于有更多时间做其他事情了,开心。