1、规格化设计的历史(附参考链接~)
1950,第一次分离,主程序和子程序的分离
产生基本的软件开发过程:分析—设计—编码—测试,使大型软件系统的开发成为可能
1975—1980,第二次分离,规格说明(Spec)和体(body)的分离
说明是类型定义和操作描述,体是操作的具体实现。
(具体的例子就是C++,Java等面向对象语言的类说明与类实现的分离。)
解决方案设计只关注说明,实现时引用或者设计体。
体的更改、置换不影响规格说明,保证了可移植性。
支持多机系统,但要同样环境。
此时产生了划时代的面向对象技术。
1995—2000,第三次分离,对象使用和对象实现的分离
基于构件开发:
标准化的软件构件如同硬件IC,可插拔,使用者只用外特性,不计内部实现。
主要参考:
http://blog.sina.com.cn/s/blog_473d5bba010001x9.html
2、规格化设计为什么得到人们的重视?
规格化设计在单人、短期、一次性项目开发中作用并不明显,但是大量的软件开发项目并不符合以上几个特点。
·对多人开发而言,好的规格设计大大增强代码可读性,便于在程序员之间共享。利于分工协作,从而提高开发效率。
·对长期、非一次性项目而言,规格设计能够减小“遗忘成本”,降低开发人员变动的代价,帮助后来的设计者更好理解函数(方法)作用而忽视具体实现细节,利于后期维护。
3、Bug分析
规格类别 | Bug原因 | 对应方法 |
(规格bug)Requires不完整 | 没有限制传入参数不为null | Test类的构造方法(5行)等 |
(规格bug)Effects不完整 | 有Modifies没有Effects | Request类的run方法(30行)等 |
(功能bug)随机行驶时未选择流量最小边运行 | 流量采用每隔500ms清零的方法 | Taxi类的waitingGo() |
(功能bug)map.txt未过滤制表符 | 直接调用LoadMap方法,没有过滤制表符 | ReadInput类的loadFile() |
(功能bug)loadfile加载地图错误 | 在重新loadfile时直接使用mi.readmap(line);Main.gui.LoadMap(mi.map, 80);而未对gui相关函数做修改 | |
(功能bug)信用输出问题 | 抢单信用度累加时刻理解错误 | Request类的addCredit() |
由上表来看,规格bug和功能bug之间并没有太大的聚集关系,很大程度上是因为设计规格和构想代码实现之间顺序颠倒,以后要在这方面多加注意。
4、不规范的JSF及改进
①没有对传入参数不能为null加以约束。
/** * @MODIFIES: rq, inq; * @EFFECTS: rq == _rq && inq == _inq; */ =================== /** * @REQUIRES: _rq != null && _inq != null; * @MODIFIES: this; * @EFFECTS: rq == _rq && inq == _inq; */
②没有对出租车各参量范围加以限制。
/** * @REQUIRES: None; * @MODIFIES: this; * @EFFECTS: sets == _s && setc == c && setx == x && sety == y && wcount == 0 && needset == true; */ ========================== /** * @REQUIRES: 0<= credit < 1000 && 0 <=x,y<80 && 0<=_s<4; * @MODIFIES: this; * @EFFECTS: sets == _s && setc == c && setx == x && sety == y && wcount == 0 && needset == true; */
③effects描述不完整
/** * @REQUIRES: detail != null; * @MODIFIES: OutputStream; * @EFFECTS: \file(E:\\detail.txt).size >= \old(\file(E:\\detail.txt)).size; */ ============== /** * @REQUIRES: detail != null; * @MODIFIES: OutputStream; * @EFFECTS: \file(E:\\detail.txt).size == \old(\file(E:\\detail.txt)).size + \old(detail).size && detail.equals(">>>>>>>>>>>"); */
④
/** * @REQUIRES: lt >= 0; * @MODIFIES: sta, begin; * @EFFECTS: \result == \old(lt)+1000; */ ======================= /** * @REQUIRES: lt >= 0; * @MODIFIES: sta, begin; * @EFFECTS: \result == \old(lt)+1000 && wcount == 0 && lastdir == Direc.NO; */
⑤
/** * @REQUIRES: lt >= 0; * @MODIFIES: posi, sta, detail, task; * @EFFECTS: \result == \old(lt)+500+waittime || \result == \old(lt)+1000; */ =============== /** * @REQUIRES: lt >= 0; * @MODIFIES: posi, sta, detail, task; * @EFFECTS: \result == \old(lt)+500+waittime && detail.equals(\old(detail)+出租车该次形式信息); */
5、心得体会
规格设计所带来的好处不言而喻,但前提是有一套逻辑严密并且同学们能够深入理解的规格体系。在这三次作业的JSF中,无论是从自己的代码,还是被测同学的代码中,都能发现一个严重的问题:要么JSF书写不完整,要么出现了大量的自然语言(而这是被禁止的)。究其原因,在这几次JSF作业中,同学们能够参考的、成体系的文档只有第九次作业下发的简略的JSF使用说明和几个简单的使用样例。但不可避免的,每人对作业实现方式弹性大,程序中个别方法功能较复杂,仅从简单的样例很难推测出正确的JSF表达方法。
另外,开始写JSF时已经实现了简单的出租车系统,即使后来的设计新增了一些功能,很多人规格设计和代码实现仍然会次序颠倒。
不过,学会规格设计的思想还是很重要的,JSF书写的练习也算是小有帮助吧。