OO--第二单元--电梯调度

OO--第二单元--电梯调度

第一次作业

目标:设计单部可稍带电梯

设计模式:生产者-消费者模式

算法:SSTF (选择最近的请求作为主请求,途中判断同方向捎带)

Controller:调度器,保存请求队列

```
private final CopyOnWriteArrayList<Customer> waitQueue;//请求队列
private boolean stop = false; // 停止标记

public synchronized void addCustomer(Customer customer);// 加入顾客
public synchronized Customer getMainCustomer(int currentFloor);// 获取主请求
public synchronized int getOne(int direction, int currentFloor);// 获取捎带请求,每次返回一个请求
public synchronized boolean outOne(int currentFloor, boolean hasOpen);//
private void printIn(Customer one, int currentFloor);
private void printOpen(int currentFloor);
private void printOut(Customer one, int currentFloor);
```

Elevator:电梯类,向调度器获取请求。在电梯类中只保留电梯操作:开关门、变换楼层

private static long TIME_MOVE = 400;
private static long TIME_DOOR = 200;
private int currentFloor;
private int goalFloor;
private Controller controller;
private int direction = 0; // >0 向上 <0 向下
private Customer mainCustomer;

public Elevator(Controller controller);
public void run();
private void goUp();
private void goDown();
private void open();
private void firstopen();
private void close();

Customer:乘客类,向调度器投放请求。

private int id;
private int from;
private int to;
private int direction;
private int status = 0; // 0表示在外,1表示在电梯里面
/* status设置目的:由于第一次作业中我将乘客的进出全部放到调度器中,所以需要在判断乘客是否在电梯中从而判断进出电梯操作 */

public Customer(int id, int from, int to);
public int getID();
public int getFrom();
public int getTo();
public int getDirection();
public int getStatus();
public void setStatus(int status);

Request:抽象到请求对象,对上面讲解的status进行设置

private int from;
private int to;
private int direction;

public Request(int from, int to, int direction);
public boolean addRequest(Customer one, int currentFloor);
Method ev(G) iv(G) v(G)
Controller.Controller() 1 1 1
Controller.addCustomer(Customer) 2 2 2
Controller.getMainCustomer(int) 3 5 6
Controller.getOne(int,int) 1 4 4
Controller.getThisFloorMain(int) 3 4 4
Controller.isStop() 1 1 1
Controller.iswaitQueueEmpty() 1 1 1
Controller.outOne(int,boolean) 5 6 7
Controller.printIn(Customer,int) 1 1 1
Controller.printOpen(int) 1 1 1
Controller.printOut(Customer,int) 1 1 1
Customer.Customer(int,int,int) 1 1 1
Customer.getDirection() 1 1 1
Customer.getFrom() 1 1 1
Customer.getID() 1 1 1
Customer.getStatus() 1 1 1
Customer.getTo() 1 1 1
Customer.setStatus(int) 1 1 1
Elevator.Elevator(Controller) 1 1 1
Elevator.close() 1 2 2
Elevator.firstopen() 1 2 2
Elevator.goDown() 1 2 2
Elevator.goUp() 1 2 2
Elevator.open() 1 3 9
Elevator.run() 3 9 10
Input.Input(Controller) 1 1 1
Input.run() 3 4 4
MainClass.main(String[]) 1 1 1
Request.Request(int,int,int) 1 1 1
Request.addRequest(Customer,int) 1 8 8
Request.getTo() 1 1 1
       
Class OCavg WMC  
Controller 2.36 26  
Customer 1 7  
Elevator 2.71 19  
Input 2 4  
MainClass 1 1  
Request 2.33 7  

1类图

各方法复杂度都较低,由于调度器对人员进出进行控制,存在可扩展性较弱问题。

时序图

时序图1

扫描二维码关注公众号,回复: 10862562 查看本文章

bug分析

未发现bug

第二次作业

目标:设计多部可稍带电梯

新增需求:1.电梯增加为多部 2.电梯人数限制 3.增加负层

设计模式:生产者-消费者模式

算法:SSTF

迭代:增加电梯线程数量即可,设计为电梯自由竞争请求,满足条件就向调度器申请请求。对前面设计进行简单修改,将乘客进出放入电梯中。调度器(非线程类)开启n个电梯线程。

电梯内增加人数限制,在取请求时判断是否可以加入。

增加负层:在1层、-1层增加特判。

类图2

Method ev(G) iv(G) v(G)
Controller.Controller() 1 1 1
Controller.addCustomer(Customer) 2 2 2
Controller.getMainCustomer(int) 3 5 6
Controller.getOne(int,int,int) 4 4 5
Controller.isStop() 1 1 1
Controller.iswaitQueueEmpty() 1 1 1
Customer.Customer(int,int,int) 1 1 1
Customer.getDirection() 2 1 2
Customer.getFrom() 1 1 1
Customer.getID() 1 1 1
Customer.getStatus() 1 1 1
Customer.getTo() 1 1 1
Customer.setStatus(int) 1 1 1
Elevator.Elevator(Controller,String) 1 1 1
Elevator.close() 1 2 2
Elevator.firstopen() 1 3 4
Elevator.goDown() 1 2 3
Elevator.goUp() 1 2 3
Elevator.isEmpty() 1 1 1
Elevator.open() 1 7 9
Elevator.outOne() 3 3 3
Elevator.printIn(Customer) 1 1 1
Elevator.printOpen() 1 1 1
Elevator.printOut(Customer) 1 1 1
Elevator.resetGoalFloor() 1 6 6
Elevator.run() 3 10 11
Input.Input(Controller) 1 1 1
Input.run() 3 5 5
MainClass.main(String[]) 1 1 1
Request.Request(int,int,int) 1 1 1
Request.addRequest(Customer,int) 1 8 8
Request.getDirection() 1 1 1
Request.getTo() 1 1 1
       
Class OCavg WMC  
Controller 2.33 14  
Customer 1.14 8  
Elevator 2.62 34  
Input 2.5 5  
MainClass 1 1  
Request 2 8  

除了Elevator.run()复杂度较高,其余方法较为简洁。

时序图

时序图2

bug分析

强测未发现bug,但是在互测中发现比较严重的bug。我在电梯人数限制方面有错误,限制7人,但是我的电梯却因为firstopen方法忘记对人数增加,导致第8个人可以进入。

在互测中也发现同屋其它同学bug,我采取了纯随机方式进行手工测试,但是也取得了不错的效果。(可能因为性能分较低,同屋的小伙伴层次都差不多吧,,ԾㅂԾ,,)

第三次作业

目标:设计多部可稍带电梯

新增:1.电梯到达楼层限制,支持换乘机制 2.动态增加电梯线程,且电梯分为ABC三种

设计模式:Worker-Thread模式

算法:Look算法

迭代:与小伙伴交流后认为look算法不易出“饿死”情况,性能更优,决定更改算法。由于更改算法,我对我的调度器与电梯类的调度算法进行了修改;将请求切割成两份,在乘客类记录换乘信息,电梯类每次取请求直接取直达请求,并对是否需要换乘、是否进行了换乘进行标记。

Controller

private final CopyOnWriteArrayList<Customer> waitQueue;
private CopyOnWriteArrayList<Elevator> elevatorPool; // 电梯线程池
private CopyOnWriteArrayList<Customer> exchangeQueue; // 换乘的请求
private CopyOnWriteArrayList<Customer> exchanging;// 正在换乘的乘客
private boolean stop = false;

public Controller();
public boolean isStop();
public boolean isEmpty();
public void start();// 开启最初的三个电梯线程
public synchronized void addCustomer(Customer customer);
public synchronized void addElevator(Elevator elevator);// 新增
public synchronized int getDirection(int currentFloor, int[] ableFloor);// 新增 判断look算法电梯运行的方向
public synchronized Customer getOne(int[] ableFloor, int direction, int currentFloor,int peopleInNum, int capacity);
public synchronized void addExchange(Customer customer);// 新增 前半段结束后放入换乘队列
public synchronized void addExchanging(Customer customer);// 新增 正在换乘的队列
public synchronized void removeExchanging(Customer customer);// 新增
public synchronized boolean hasPassenger(int[] ableFloor, int currentFloor,int direction);// 新增 电梯里面空后判断在同方向是否还有可捎带对象
public boolean scanFloor(int floor, int[] ableFloor);// 新增 寻找是否在可以到达的楼层中

Customer

private int id;
private int goalFrom;
private int goalTo;
private int exchangeFloor;
private int direction;
private boolean exchange = false;
private boolean haveExchanged = false;

public Customer(int id, int goalFrom, int goalTo);
private void needExchange();// 新增 电梯换乘记录
// 其余getter & setter算法未展开
Method ev(G) iv(G) v(G)
Controller.Controller() 1 1 1
Controller.addCustomer(Customer) 2 2 2
Controller.addElevator(Elevator) 1 1 1
Controller.addExchange(Customer) 1 1 1
Controller.addExchanging(Customer) 1 1 1
Controller.getDirection(int,int[]) 6 15 16
Controller.getOne(int[],int,int,int,int) 8 12 13
Controller.hasPassenger(int[],int,int) 1 20 26
Controller.isEmpty() 1 3 3
Controller.isStop() 1 1 1
Controller.isexchangeQueueEmpty() 1 1 1
Controller.isexchanging() 1 1 1
Controller.iswaitQueueEmpty() 1 1 1
Controller.removeExchanging(Customer) 1 1 1
Controller.scanFloor(int,int[]) 3 1 3
Controller.start() 1 2 2
Customer.Customer(int,int,int) 1 1 2
Customer.getDirection() 3 1 5
Customer.getExchangeFloor() 1 1 1
Customer.getGoalFrom() 1 1 1
Customer.getGoalTo() 1 1 3
Customer.getId() 1 1 1
Customer.isExchange() 1 1 1
Customer.isHaveExchanged() 1 1 1
Customer.needExchange() 1 1 33
Customer.setHaveExchanged(boolean) 1 1 1
Elevator.Elevator(Controller,String,String) 2 2 5
Elevator.close() 1 2 2
Elevator.firstOpen() 2 7 9
Elevator.goDown() 1 2 3
Elevator.goUp() 1 2 3
Elevator.isEmpty() 1 1 1
Elevator.open() 1 11 13
Elevator.outOne() 3 3 3
Elevator.printIn(Customer) 1 1 1
Elevator.printOpen() 1 1 1
Elevator.printOut(Customer) 1 1 1
Elevator.run() 6 17 19
Input.Input(Controller) 1 1 1
Input.run() 3 6 6
MainClass.main(String[]) 1 1 1
SafeOurPut.println(String) 1 1 1
       
Class OCavg WMC  
Controller 3.31 53  
Customer 2.9 29  
Elevator 3.75 45  
Input 3 6  
MainClass 1 1  
SafeOurPut 1 1  

由分析可见:我本次作业中Controller.getDirection(int,int[])Controller.getOne(int[],int,int,int,int)Controller.hasPassenger(int[],int,int)负责度较高,因为这几个方法不仅遍历了请求队列,而且每次都需判断楼层,导致复杂度较高。

类图3

1.分析换乘策略,可以发现楼层特点,分为电梯运行方向进行分析。可以直达则不标记换乘位,如过不可直达怎在1层或15层换乘。其中可以发现3层比较特殊,会发生折返情况。分别在1层和5层发生换乘。

2.look算法:放弃主请求思想,改为电梯状态即电梯运行方向分析。

当方向为0即暂停,则寻找下一请求,若请求进入楼层高于当前楼层,则向上;若低于,则向下;若恰好相等,则以该请求运行方向作为电梯运行状态方向。

当方向为1或-1即向上或向下,如若电梯内的等待队列不空,则继续执行;若已经空了,但同方向还有可能捎带请求,继续保持原方向;若已经没有请求,则设置方向为0,进行上面一步的判断。

3.解决cpu超时问题:本次作业需要注意,仅仅判断请求队列是否为空、是否还有换乘正在进行,还需要注意在队列不为空的时候,由于电梯到达楼层有限,很多电梯也无法获取请求,处于空转状态。所以每次需要判断是否本电梯可稍带,调用wait()。

时序图

时序图3

bug分析

本次作业可以说是我最难过的一次,强测有6个点因为换乘策略有一种情况不对,导致出现线程无法结束的情况。可以说非常不应该,我对此也难过了很久(不得不承认我是个脆弱的女孩子/(ㄒoㄒ~)/)其余通过的点的性能分基本满了....(不过又有什么用呢/(ㄒoㄒ)/

互测本姑娘一直采取人不犯我,我不犯人的策略,但是这次是对方在最后2小时对我出击,导致我一直以为自己并没有被hack。因此并没有对他人回刀。唉....不提了....睡了睡了.....(~﹃~)~zZ

SOLID分析

SRP:本次作业每个类都有明确的职责,乘客类决定换乘、电梯线程进行请求的处理、调度器进行接受请求和提供合理请求、输入线程向调度器输入请求

OCP:除调度器和电梯外其它类很好满足了开闭原则。但是三次作业的迭代开发可见,调度器和电梯每次增加新功能,都要进入电梯和调度器进行open改动。

LSP:由于本次作业我并没有设计除线程继承以外的继承关系,所以符合LSP规则,未对父类进行引用

ISP:未设计接口,程序可扩展性还是较差。改进方案可以是每一类电梯单独享有一个调度器,继承自主调度器。如此每新增一种电梯类型,并无需对主调度器进行open

DIP:未设计抽象类。

扩展性分析

通过上文分析可见,我是一个功利girl。本次作业更多的是以完成为目标,但是忽略了很多课上讲到的设计模式原则。在大工程方面达不到很好的扩展性。

改进方案

每一类电梯单独享有一个调度器,继承自主调度器。如此每新增一种电梯类型,并无需对主调度器进行open,直接增加调度器类型。

为电梯做一个接口,每类电梯都继承自这个Elevator接口。定义电梯的上升下降、开关门以及乘客进出。

为乘客做一个抽象类,乘客可分为换乘与不换乘,以及可能存在的愿意走楼梯的类型。

心得体会

这三次作业对我来讲最痛苦的莫过于第一次作业,反复出现死锁,对多线程理解困难。但是突破这个瓶颈后,这三次作业就不难向下推进。但是令我最心痛的是第三次作业,错的实在是不应该,还是太过相信中测,以后过了中测也要多测试。

在完成作业的过程中,我并没有很好的注意到可扩展性的设计。为了便于完成,我还是采取了“面向完成”的设计方法,用了比较直白的面向对象思想。但是完成后再进行反思,可以看到还有很多值得改进的地方。

不要太相信中测!不要太相信中测!不要太相信中测!

最后,愿OO温柔以待~~~❤

猜你喜欢

转载自www.cnblogs.com/zhangjiayuan/p/12715572.html