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的代码作业,好像并没有什么特别的感觉,以后终于有更多时间做其他事情了,开心。

posted @ 2018-05-28 00:04  ignautics  阅读(220)  评论(0编辑  收藏  举报