zoukankan      html  css  js  c++  java
  • OO第二单元总结:电梯

    一、问题描述

    ​ 本单元作业主要为多线程入门。具体作业为电梯调度——目的选层式电梯,即乘客的请求同时包含了位置楼层和目标楼层。三次作业总体的需求差异不大,在第三次增加了多电梯和关于停靠楼层和电梯人数的限制,主要导致了换乘需求的出现。

    事实上,第二次作业完全复制了第一次作业,第三次增加了一点微小的改动(复制了三次)


    二、程序设计

    整体构架

    ​ 几乎完全模拟现实生活中的电梯。

    ​ 典型的生产者-消费者模型。输入为生产者,电梯为消费者,大楼为托盘。在多电梯时增加一个(分解者)调度器用来抛请求。

    ​ 整体的思路是使用队列来管理每一个楼层的请求。电梯分别拥有一个嵌套_ArrayList_来管理相应楼层的请求队列。楼层号用数组存,有效下标从1开始(方便继承第一次作业的写法)。注意楼的下标为出发楼层,而电梯的下标为目标楼层

    ArrayList<ArrayList<Request>> waitingLine = new ArrayList<>(24);

    ArrayList<ArrayList<Request>> passenger = new ArrayList<>(24);

    ​ (对于使用的数据结构,_ArrayList_其实并不是一个好的选择——线程不安全且为数组,不利于增删操作。)
    多线程:sleep+轮询:简单粗暴不会出错。

    乘客:Request

    ​ 方法:出入电梯;

    换乘众所周知,我们写的电梯里的都是假人 然而出于对真实电梯情况的模拟,我还是将换乘的策略放到了Request里而非交给调度器。因为当乘客处于电梯里时,无法获取其他电梯的运行状态。

    ​ 一个朴素的换乘策略如下表(前提:有换乘必要):

    起使楼层 目标楼层 途经楼层
    -3~-1, 3 ANY 1
    2~15(不含3) ANY 最近不为3的奇数层
    2~15(不含3) -3 1
    2~15(不含3) 16~20 15
    16~20 ANY 15
    • 如何管理换乘请求:

      • 保证顺序:先执行第一步,再执行第二步。

      • 管理方案:初始请求和换乘请求作为两个无差别请求,在第一个请求完成之后,改变fromto的值,作为船新的请求被抛回请求队列,保证了时间的先后顺序。

        好处:换乘与否与其他类无关,可顺畅复用之前代码。

    电梯:Elevator

    • 的交互:

      • 当电梯内没人时,需要设置到楼的任务结束标志(第三次的三电梯需要)。
      • 从电梯拿到Request。
    • 运行算法:LOOK

      ​ 与实际的最常见的运行逻辑一致:在需要运行到的最高层和最底层来回走动,每次开关门时更新最高最低楼层。

      接人策略:前两次与第三次略有不同。开门的条件都是一致的,即有人要下有同方向的人要上。不同之处在于,前两次一旦开门就要把该楼层所有的人接进来。而第三次由于有容量限制,即使开门也只能接与运行方向一致的乘客,避免以后“挤不上”。且需要先下后上

    楼:Building

    ​ 主要是管理需求队列和统一控制终止信号。

    调度器(伪):Dispatcher

    ​ (没有写优化的调度器只是没有感情的 丢 人 机器)

    ​ 获取一个请求,根据预先初始化的数组判断有哪些电梯可乘坐(请求已被拆分,所以至少有一个电梯可乘坐)。在其中选择一个人数少的,丢进去。


    三、BUG分析

    • 来回跑不接人问题

      现象:当高层的人要往上走,低层的人要往下走时,电梯会开始自闭在两端来回跑而不开门接人。

      原因:到极高和极低层的时候还没来得及改变方向,拒绝接受方向不一致的人。

      解决:在最高和最低点多判断一次。

    • 异常终止问题
      现象:提前终止或者不终止。
      原因:终止位设置不全面。
      解决:将输入、楼层人数、电梯人数的结束信号都放到里,各设备需要时自取。

    • 三楼停靠问题
      现象:不该在三楼停的电梯在三楼停下。
      原因:拆分请求到最近奇数楼的时候忘了3楼为最近奇数楼的情况(好傻)。
      解决:特判一下。


    四、代码分析

    第一次作业

    • 代码规模

    • 复杂度

    • 类图

    第二次作业

    • 代码规模

    • 复杂度

    • 类图

    第三次作业

    • 代码规模

    • 复杂度

    • 类图

    SOLID原则

    • Single Responsibility Principle(单一职责原则):每个类只完成自己对应的功能,每个方法只含有单一功能。
    • Open Close Principle(开放封闭原则):三次作业没有重构,几乎直接复用模块,但免不了为了适应不同情况对代码以及方法进行一定的修改。(比第一单元次次重构要好不少)
    • Liscov Substitution Principle(里氏替换原则):(貌似还未涉及到)。
    • Interface Segregation Principle(接口分离原则):(未涉及)。
    • Dependency Inversion Principle(依赖倒置原则):对于电梯类来说,做得还不够好。第三次由于电梯类的设计不够抽象,为了一些微小但繁琐的修改重写出了三个电梯类……。

    五、总结

    ​ 最初的难点在于开始多线程编程。理解之后程序就变得容易编写起来。

    ​ 这次做得最好的是没有照搬指导书上的算法,而是按照真实电梯的思路去设计。在第一次电梯用的时间长一点,但之后几乎没有花费什么功夫,并且性能还不错(可惜第三次手滑出了bug)。对于面向对象的理解,也比第一次更加清晰了。

  • 相关阅读:
    《构建之法》心得体会
    简单工厂模式加减乘除器
    个人简介
    单元测试和代码覆盖率工具的使用
    Bookstore系统缺陷报告
    《构建之法读后感》
    3137102432_ 施少兵_ lab3
    3137102432_施少兵_实验2
    个人简介
    第六次作业:购物系统缺陷
  • 原文地址:https://www.cnblogs.com/DilemmaR/p/10758438.html
Copyright © 2011-2022 走看看