OO第三次作业总结

规格化设计发展史

   20世纪50年代, 软件伴随着第一台电子计算机的问世诞生了。 出现了专门以写软件为职业的人。
   在计算机系统发展的初期, 硬件通常是执行一个单一的程序, 而这个程序又是为一个特定的目的而编制的。 早起当通用硬件成为平常事情的时候, 软件的通用性却是很有限的。 那时的软件往往带有强烈个人色彩。 早起的软件开发也没有什么系统方法可循。 除了源代码外往往没有软件说明书等文档。
   从60年中期到70年代中期是计算机系统发展的第二个时期, 软件开始作为一种产品被广泛使用。 随着软件数量的急剧膨胀与需求的日趋复杂, 维护的难度越来越大, 早期的个体化软件开发方式明显已不再适用。
   1968年北大西洋公约组织的计算机科学家在联邦德国召开的国际学术会议上第一次提出了“软件危机”(software crisis)这个名词。1968年秋季,NATO(北约)的科技委员会召集了近50名一流的编程人员、计算机科学家和工业界巨头,讨论和制定摆脱“软件危机”的对策。
   人们开始研究如何用系统化、规范化、数量化等工程原则和方法去进行软件的开发和维护。这方面的重要成果就是在70年代风靡一时的结构化开发方法,即PO(面向过程的开发或结构化方法)以及结构化的分析、设计和相应的测试方法。也就是规格化设计的诞生。
   规格化设计能够减少设计复杂性, 简化复杂问题。 它较适合于开发数据处理类型软件的需求分析, 规范地表达需求,简明、易读,也易于使用,为后一阶段的设计、测试、评价提供了有利的条件。也就被人们广泛的接受和使用。

规格化BUG

作业 规格BUG数 规格BUG名称 影响行数
9 0
10 1 JSF不符合规范 10
11 0

规格bug产生原因

   这几次遇到的都是非常好心的测试者, 在此先感谢一波。唯一被指出规格化BUG的问题是第10次作业, 有一次JSF不符合规范。 具体到是某一个方法的Effects中, “==”漏写成了“=”, 因为规格的描述都得是布尔表达式, 不能出现赋值语句的。 这里是本人笔误, 自己在检查了一番, 把类似漏写的地方都修正了。

规格写法改进

前置条件

//1. 前置条件得是布尔表达式
/** before:
   * @REQUIRES: a > 0, b > 0; 
   */
/** after:
   * @REQUIRES: a > 0 && b > 0; 
   */

//2. 再简单的函数也不要轻易地写none, 小心null的情况可能crash
/** before:
   * @REQUIRES: none; 
   */
boolean compareStringLength(String a, String b){
    return a.length() < b.length();
}
/** after:
   * @REQUIRES: a != null && b != null; 
   */

//3. 传入集合元素的时候, 不要忘记对每个元素的限制
/** before:
   * @REQUIRES: a != null; 
   */
long sumOfStringLength(ArrayList<String > a){
    long ret = 0;
    for (int i = 0; i < a.size(); i ++)
        ret += a.get(i).length();
    return ret;
}
/** after:
   * @REQUIRES: a != null && (all int i; 0 <= i < a.size(); a.get(i) != null);
   */

//4. 也可能要考虑数值范围
/** before:
   * @REQUIRES: none; 
   */
int add(int a, int b){
    return a + b;
}
/** after:
   * @REQUIRES: INT_MIN <= a + b <= INT_MAX;
   */

//5. 从第10次作业起, 还要考虑repOK, 方法的调用不能改变对象的有效性
boolean repOK(){return this.a > 0;}
/** before:
   * @REQUIRES: none; 
   */
void setA(int _a){this.a = _a;}
/** after:
   * @REQUIRES: _a > 0;
   */

后置条件

//1. 不要把等于写成赋值
/** before:
   * @EFFECTS: src == _src; dst == _dst; T = _T; 
   */
Req(Point _src, Point _dst, long _T){
    src = _src; dst = _dst; T = _T; 
}
/** after:
   * @EFFECTS: src == _src; dst == _dst; T == _T; 
   */

//2. 过度的自然语言
/** before:
   * @EFFECTS: return the min element; 
   */
int findmin(int arr[], int size){
    int pos = -1;
    for (int i = 0; i < size; i ++)
        if (pos == -1 || arr[pos] > arr[i]) pos = i;
    return arr[pos]; 
}
/** after:
   * @EFFECTS: (
esult == a) ==> (all int i; 0 <= i < size; arr[i] >= a);
   */

//3. 不要忘记对异常的抛出
/** before:
    * @ EFFECTS: 
    * (
esult==a)==>(all Account a;(	his.contains(a)==true)&&a.getAccountId()==accountId);
    */
public Account queryByAccountId(String accountId) throws InvalidOperationException{
    Account a = null;
    for (int i = 0; i < accounts.size() && a == null; i ++) {
	if (accounts.get(i).getAccountId().equals(accountId)) a = accounts.get(i);
    }
	   
    if (a == null) {
        throw new InvalidOperationException("Invalid Account!");
    }
	   
    return a;	      
}
/** after:
    * @ EFFECTS: 
    * (
esult==a)==>(all Account a;(	his.contains(a)==true)&&a.getAccountId()==accountId);
    * (all Account a;a.getAccountId()==accountId && (	his.contains(a)==false))==>exceptional_behavior("Invalid Account!") ;
    */
   
//4. 不要直接去描述算法
/** before
   * @EFFECTS: arr[0] == (old(arr[0]) ^ old(arr[1]) ^ old(arr[0]) ^ old(arr[1]) ^ old(arr[1]));
   *                   arr[1] == (old(arr[0]) ^ old(arr[1]) ^ old(arr[1]));                     
   */
void swapTheFirstTwoElement(int[] arr){
    arr[0] = arr[0] ^ arr[1];
    arr[1] = arr[0] ^ arr[1];
    arr[0] = arr[0] ^ arr[1];
}
/** after
   * @EFFECTS: arr[0] == old(arr[1]) && arr[1] == old(arr[0]);                  
   */

//5. 可以使用Java程序中的常用方法来帮助表达
/** before
   * @EFFECTS: 
esult == (exits int i; 0 <= i < size; arr[i] == x);                  
   */
boolean isIn(int[] arr, int size, int x){
    for (int i = 0; i < size; i ++)
        if (arr[i] == x) return 1;
    return 0;
}
/** after
   * @EFFECTS: 
esult == arr.contains(x);                 
   */

聚集关系分析

作业 方法名 规格BUG数 功能BUG数
9 Move.ramble() 0 1
9 Scheduler.run() 0 1
10 Req.Req() 1 0
10 Req.Same() 1 0
10 Scheduler.run() 0 1
11 Move.ramble() 0 1
11 Scheduler.run() 0 1

   从表格上看聚集关系并不明显, 规格Bug和功能Bug并无什么交集, 主要是因为规格基本上都是在方法实现后写的, 出错概率不大。

撰写和设计规格的基本思路与体会

   由于OO作业的特性, 规格基本上都是在方法完成后补写的, 所以基本是通过方法来写规格。 由于课程要求每个方法的要写规格, 个人感觉有几个地方就显的很难受, 一个是写构造方法的时候, 构造方法基本就是用传入的参数对类成员变量一一赋值, 本身就是若干个赋值语句, 然后他的effects写出来基本就是把方法中的等号写成两个罢了, 显得很累赘。 还有就是存在许多调用的方法, 根据模块化设计的思想, 应当做到尽量细化每个类每个方法的职责, 这就难免方法数的增加, 也肯定少不了方法的调用, 在函数的层层嵌套下, 如果调用函数还要重复的描述被调用函数的规格的话, 这样的规格写下去恐怕得比函数本身还要大了, 这个时候就不得不去加以自然语言。
   我也是通过OO第一次接触规格这种东西。 规格在实际的工程中想必是有许多好处的, 写好规格有利于对代码本身的理解, 可以说是规范化的注释, 同时他也不会去限制代码的实现, 就像是一个针对一小段代码的需求限制。 良好的规格书写习惯有利于工作之间的交流, 希望这个能让自己在今后的工作中大有裨益吧。

原文地址:https://www.cnblogs.com/zhysora/p/9104766.html