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)。对于面向对象的理解,也比第一次更加清晰了。

猜你喜欢

转载自www.cnblogs.com/DilemmaR/p/10758438.html