zoukankan      html  css  js  c++  java
  • OO第三次作业 Care for a ride?

    OO第三次作业 Care for a ride?

    Care for a ride?是《海上钢琴师》中我最喜欢的一句台词,在一个风雨交加的夜晚,1900和他的小号手朋友一起,松开钢琴的轮锁,让钢琴在甲板上随意滑行,所以这四次出租车作业,我一直将“Care for a ride?”作为我的README题目。

    规格设计发展史

    为什么要写规格?

    程序,是给机器看的,规格,是给人看的。为什么要在给机器看的东西上写给人看的东西呢,是因为工程的需要。工程是复杂的,长期的,多人合作的。因此,不仅仅你的代码需要实现功能,你也需要维护代码的可读性,你也需要辅助自己和他人,了解每个类每个方法的功能,这样他人才能维护你的代码,你的代码才不是“日抛型”代码,而可以持续的应用于生产活动当中。

    并且,规格化设计也将辅助你从“意识流”设计,想什么写什么,变到标准化的设计。“To err is human.”不为自己定些游戏规则,人肯定会犯错的(虽然定了法律也有人犯罪)。

    最初的程序设计是直接面向机器、阅读困难,后来出现了面向过程的设计思想。对于面向过程设计思想的变革,goto语句成为讨论的热点。Dijkstra于1968年发表著名的《GOTO有害论》,引起了广泛的关注,结构化的设计思想也在此时应运而生。结构化设计思想,是指的是通过“自顶向下、逐步细化、模块化”的方法进行程序设计。这种设计思想旨在控制各个模块的程序复杂度,从而保证整体工程的正确性。面向过程的设计存在着扩展性不好的弊病,由此,产生了面向对象的设计模式。程序设计的模块化体现的更为明显,各个部分更加独立,相应地,各个模块之间的交互也更加重要。

    无论是面向过程还是面向对象,只要存在模块之间的交互,就必然存在着规格规范的问题。就个人感觉来说,规格是说明了模块的接口,说明了模块的IO要求。所以,规格化是模块化的发展的必然需求。

    规格bug及其分析

    主要有两个bug,一个是JSF规格书写,一个是repOK方法书写:

    JSF 规格书写bug

    Guideline1:

    前置条件必须是布尔表达式。

    在后置条件描述中,每个@EFFECTS后跟着的都应该是一个可判定的布尔表达式,且应该只使用集合论和一阶逻辑谓词来表示的布尔表达式,从而有效描述方法的设计约束。虽然也可以采用自然语言描述规格,但会不可避免的引入二义性

    备注:被 JSFTool 测试除了了问题。

      
    C:oo esteesrcoo11InputHandler.java:47: 警告 - oo11.InputHandler.run() : "System.in"
    Warning 95 : @requires (pre-condition) must be a boolean expression!
    (Maybe this is a natrual language boolean expression...)

    这个地方使用了成员变量描述 requires ,不是逻辑表达式、也不是自然语言,java 也没有 c++ 一样的对对象的非 null 判断,因而错误。

      C:oo	esteesrcoo11TrafficLight.java:36: 警告 - 
    oo11.TrafficLight() : "total_num = 0"
    Warning 4 : @effects (post-condition) must be a boolean expression!
    (Maybe this is a natrual language boolean expression...)

    C:oo esteesrcoo11TrafficLight.java:36: 警告 -
    oo11.TrafficLight() : "
          pos = new Position[MAXN]"
    Warning 5 : @effects (post-condition) must be a boolean expression!
    (Maybe this is a natrual language boolean expression...)

    以及很多构造函数、setter 的 effects,直接使用了赋值,没有使用逻辑表达式、也不是自然语言,仅仅只描述了算法。

    repOK 方法书写bug

    针对构造方法,初始状态repOK为真。

    备注

      package oo11;

    public class TestMain {
    public static void main(String[] args) {
    Car car = new Car(40);
    try {
    System.out.println(car.repOK());
    } catch(Throwable e) {
    e.printStackTrace();
    }
    TraceableCar carB = new TraceableCar(5);
    try {
    System.out.println(carB.repOK());
    } catch(Throwable e) {
    e.printStackTrace();
    }
    Request req = new Request();
    try {
    System.out.println(req.repOK());
    } catch(Throwable e) {
    e.printStackTrace();
    }
    }
    }

    这是测试两个 Car 的 repOK 和 Request 无参数构造的 repOK 的主程序,repOK 没有返回 true,原因是 from 数组构造后新建了数组,但是里面的值基本都是 null,但是 repOK 中调用了 fromi.isValid(),导致 nullpointer。

    再有是 Position 的带参构造,没有检查坐标的范围。

    测试用代码:

      package oo11;

    public class TestMain {
    public static void main(String[] args) {
    Position pA = new Position(80, 80);
    System.out.println(pA.repOK());
    Position pB = new Position(-1, -1);
    System.out.println(pB.repOK());
    Request reqA = new Request("CR", pA, pB);
    System.out.println(reqA.repOK());
    }
    }

    还有很多像是,Information 和 Main 的 repOK 没写(return true;)之类的问题。

    不好的条件写法及其改进

      修改前:
    public void addNearby(Long now_time, Request now_req) {
    /**
    * @REQUIRES: exists cars[i].getStatus == Car.CarStatus.waiting
    * @MODIFIES: now_req
    * @EFFECTS: 将符合抢单条件的车辆加入到now_req的备选集合中
    */

    修改后:
    public void addNearby(Long now_time, Request now_req) {
    /**
    * @REQUIRES: exists cars[i].getStatus == Car.CarStatus.waiting
    * @MODIFIES: now_req
    * @EFFECTS: all Integer i, req.set.cotains(cars[i]) ==> (cars[i] can answer req)
    */
      
    修改前:
    public ArrayList<Integer> getCarAtStatus(Car.CarStatus status) {
    /**
    * @EFFECTS: 返回处于status状态的Car的下标集合
    */

    修改后:
    public ArrayList<Integer> getCarAtStatus(Car.CarStatus status) {
    /**
    * @EFFECTS: esult = ArrayList<Integer> set, all Integer i, set.contains(cars[i]) ==> (cars[i].status == status)
    */
      修改前:
    public void loadRequest(String file_path) {
    /**
    * @REQUEIRES: file_path!=null
    * @MODIFIES: Q
    * @EFFECTS: 读取Loadfile中的Request,并加入请求队列中。
    */

    修改后:
    public void loadRequest(String file_path) {
    /**
    * @REQUEIRES: file_path!=null
    * @MODIFIES: Q
    * @EFFECTS: all Request req, Q.contains(req) ==> (Loadfile.contains(req) )
    */
      
    修改前:
    public void openRoad(Position now_src, Position now_dst) {
    /**
    * @REQUEIRES: (now_src.getX() >= 0 && now_src.getX() <80) &&
    *   (now_dst.getX() >= 0 && now_dst.getY() <80);
    * @MODIFIES: dig;
    * @EFFECTS: 打开(now_src,now_dst)对应的路;
    */

    修改后:
    public void openRoad(Position now_src, Position now_dst) {
    /**
    * @REQUEIRES: (now_src.getX() >= 0 && now_src.getX() <80) &&
    *   (now_dst.getX() >= 0 && now_dst.getY() <80);
    * @MODIFIES: dig;
    * @EFFECTS: all Integer i,j; (dig[i][j] == 1) ==> (dig[i][j] in (now_src, now_dst))
    */
      
    修改前:
    public synchronized void remove(Request now) {
    /**
    * @REQUEIRES: (queue != null);
    * @MODIFIES: None;
    * @EFFECTS: 从queue中删除当前请求。
    * @THREAD_REQUIRES: locked(this);
    * @THREAD_EFFECTS: locked();
    */

    修改后:
    public synchronized void remove(Request now) {
    /**
    * @REQUEIRES: (queue != null);
    * @MODIFIES: None;
    * @EFFECTS: (forall Request req; req != now; queue.contains(req) == old(queue).contains(req));
    * @THREAD_REQUIRES: locked(this);
    * @THREAD_EFFECTS: locked();
    */

    功能bug及其与规格的联系

    第11次指导书中:

    b) 可追踪出租车扩展普通出租车的路径选择方法,使得可追踪出租车能够行走关闭的道路(普通出租车则不可以)。注:关闭的道路的车流为0,且保持不变。

    测试检查对于关闭的道路,可追踪出租车是否会将被关闭的道路的流量记为0。

    结果输出算得的最小流量为 2,并下一步走到流量更大的边 (0,0) -> (1,0)。但根据指导书,被关闭的边 (0,0) -> (0,1) 流量更小、为0。bug 源于我使用了GUI中的流量,但是我忘记修改了本次新GUI的代码,导致对于被关闭的道路 getFlow 时,得到的流量不一定为 0:

      
    public static int GetFlow(int x1, int y1, int x2, int y2) {// 查询流量信息
    synchronized (guigv.memflowmap) {
    return guigv.memflowmap.get(Key(x1, y1, x2, y2)) == null ? 0 : guigv.memflowmap.get(Key(x1, y1, x2, y2));
    }
    }

    其与规格之间的联系,在于我没有明确规格中的流量是怎么来的,而忘记了使用新GUI时造成的流量维护错误。

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

    最初我真的觉得写规格一点用也没有,因为据我所知,大家都是在写完代码之后写规格·····

    这样的规格意义就不大了,因为规格是用来设计的,而不是用来debug或者完成任务的,否则那还不如直接写两句注释。

    直到在OO第六次上机当中,我们的任务是完成一个银行账户管理系统,而在这个系统中,有一个类的方法全部都是空的,只有规格,但是规格很完备(老师写的和同学写的是不一样)。

    规格用布尔表达式式写成,我发现基本上根据规格写出代码是一件非常容易的事情,而且,写明前置和后置条件,应该可以避免很多由于疏忽造成的bug,想起了高中时写函数,老师逼着你写定义域一样,不在定义域内的计算都是不合理的,不明确前置后置条件的方法,都是耍流氓的方法。

     

     

  • 相关阅读:
    Pandas学习整理与实践
    数据描述性统计整理
    关于购置硬盘的相关注意点
    福大软工 · 最终作业
    福大软工 · 第十二次作业
    Beta冲刺 (7/7)
    Beta冲刺 (6/7)
    深度剖析Vue中父给子、子给父、兄弟之间传值!
    mysql 增删改插
    前端必学TypeScript之第一弹,st基础类型!
  • 原文地址:https://www.cnblogs.com/SomedayWill/p/9106992.html
Copyright © 2011-2022 走看看