OO第二单元--电梯调度

第一次作业-单部多线程可捎带电梯

本次作业采取了生产者-消费者模式进行设计,主要构造了输入器,调度器,电梯三个类,其中输入器为生产者,不断向托盘也即调度器发送请求。而电梯同时也不断向调度器获取请求。当输入器不再获取输入且调度器中的请求队列为空时,并且电梯将接送的乘客均送到目的楼层时,电梯停止运行。在本次设计过程中,调度器实质功能较少,基本上只具有一个共享队列的功能,主要的运行策略及捎带策略均集成到了电梯的内部。其中捎带策略选取的是标准的ALS策略。本次设计类图如下:

第二次作业-多部多线程可捎带调度电梯的模拟

本次作业相较于第一次作业在开始时引入了多部电梯并行运行。因此若是仍然是采用第一次的生产者-消费者模式,让电梯不断地向调度器请求请求,则失去了其调度意义。因此本次作业采用了生产者也即输入器不断向调度器发送请求,而调度器不断向各个电梯分派请求。而电梯中存在两个队列:要去接的乘客队列和正在送的乘客队列。由调度器分派任务后,请求进入电梯中要去接的乘客队列,在电梯到达特定楼层时经过判断后要去接的乘客进入电梯内,实现接送。本次相较于第一次作业而言,电梯内部保持了较高的功能独立性,同时在电梯内部的运行算法方面也进行了进一步的优化,将分派任务与执行任务分配给调度器和电梯内部,实现了一定程度上的高内聚性。本次设计类图如下:

 第三次作业-多部多线程可捎带调度电梯

本次作业相较于上一次作业,存在如下难点及拓展:①电梯可以运行的楼层受到限制②在运行过程中,电梯可以动态增加。针对这两个拓展,分别采取解决方案如下:①由于电梯可以运行的楼层受到限制,因此必须引入换乘机制,也即乘客可能会乘坐多部电梯,经过换乘后才能到达目的地②针对动态增加机制,在调度器中引入了线程池,将电梯均存于调度器中,由调度器唤醒。在这次设计过程中,很好地实现了面向对象思想中的迭代思想,主体部分均是基于第二次作业,仅在较少的地方进行修改,其余均为基于第二次作业的拓展功能。本次设计仍然保持了电梯内部运行功能的高度集中性,但调度算法采取了一定的修改,采用了一定程度上的贪心算法,也即尽量减少换乘的次数。同时考虑到了乘客可能换乘的情况,引入了Person类,将乘客分为了可以直达的乘客和需要换乘的乘客,需要换乘的乘客在进入电梯时设置暂定楼层也即换乘楼层,在到达换乘楼层时乘客离开电梯,同时将其发往调度器,由调度器将其分配给接送他的电梯。本次作业的类图如下:

方法复杂度如下:

Controller.addElevator(Elevator) 1.0 1.0 1.0
Controller.Controller() 1.0 1.0 1.0
Controller.InputNotEnd() 1.0 1.0 3.0
Controller.Put(Person) 7.0 6.0 7.0
Controller.putElevator(String,String) 1.0 4.0 4.0
Controller.putPerson(Person) 1.0 1.0 1.0
Controller.putRequest(PersonRequest) 1.0 1.0 1.0
Controller.randomPut(Person) 1.0 1.0 1.0
Controller.runAll() 1.0 2.0 2.0
Controller.setKeepInput(boolean) 1.0 1.0 1.0
Elevator.checkFull() 1.0 1.0 1.0
Elevator.checkOpen(int) 7.0 4.0 7.0
Elevator.checkOpenAble(int) 3.0 2.0 14.0
Elevator.Elevator(Controller,String,String) 1.0 3.0 4.0
Elevator.getCurrentFloor() 1.0 1.0 1.0
Elevator.getDirection() 1.0 1.0 1.0
Elevator.getTargetFloor() 1.0 1.0 1.0
Elevator.keepRun() 1.0 2.0 2.0
Elevator.move() 1.0 2.0 3.0
Elevator.open() 1.0 2.0 2.0
Elevator.outAndIn() 7.0 10.0 12.0
Elevator.putRequest(Person) 1.0 2.0 2.0
Elevator.run() 1.0 7.0 7.0
Elevator.setDirection() 1.0 1.0 3.0
Elevator.setRun() 1.0 13.0 13.0
InputDealer.InputDealer(Controller) 1.0 1.0 1.0
InputDealer.run() 3.0 5.0 6.0
MainClass.main(String[]) 1.0 1.0 1.0
Person.getCurrentFloor() 1.0 1.0 1.0
Person.getFromFloor() 1.0 1.0 1.0
Person.getPersonId() 1.0 1.0 1.0
Person.getTempToFloor() 1.0 1.0 1.0
Person.getToFloor() 1.0 1.0 1.0
Person.Person(PersonRequest) 1.0 1.0 1.0
Person.setCurrentFloor(int) 1.0 1.0 1.0
Person.setTempToFloor(int) 1.0 1.0 1.0
Total 58.0 86.0 111.0
Average 1.6111111111111112 2.388888888888889 3.0833333333333335

类复杂度如下:

Controller 1.8 18.0
Elevator 3.7333333333333334 56.0
InputDealer 3.0 6.0
MainClass 1.0 1.0
Person 1.0 8.0
Total   89.0
Average 2.4722222222222223 17.8

就本次架构的可拓展性来讲,由于保持了电梯运行的高度独立性,因此可以在其后引入新型电梯,同时可以增加更多的电梯。而就调度器而言,也可以采用性能更佳的调度器,而无需修改电梯内部的运行逻辑。

BUG分析

对于第一次作业而言,由于采用了while循环轮询的方式,当调度器一直没有接收到新的请求时,电梯没有采用wait操作,这就导致了在互测中产生了CPU超时的问题。

对于第二次作业而言,由于没有弄清楚System.in的机制,在主线程和输入器中均新建了System.in,这就导致有部分数据没能进入输入器,最终也导致了没能进入互测阶段。

对于第三次作业而言,出现了部分乘客在换乘后没能正确被接送到目的楼层的情况,后经分析发现是当乘客到达换乘楼层后所有电梯都关闭了的原因,因此在其后引入了translate标签,仅有换乘的乘客为0的情况,且满足之前的停止情况时电梯才会停止运行。

查找BUG策略

在查找BUG过程中,使用了python的os库和subprocess库,由subprocess库模拟输入。由于在测试数据上,仅仅只做了基本的功能性测试,因此没有发现过别人的BUG

对比和心得体会

在这三次作业中,更多地体会到了面向对象的编程思想。在设计时也更多地考虑到了一定的可拓展性,其中尤其是从第二次作业到第三次作业,并没有进行过多的修改,而只是基于原有构架的拓展,同时对于设计模式的六大原则,在设计过程中也有一定的考量,因此通过这几次作业仅就面向对象编程来讲还是有了更深入的理解。同时对于多线程编程,通过这几次的作业包括实验课,学会了较为常见的一些模式,如观察者模式,生产者消费者模式,以及Worker-Thread模式。对于加锁,解锁,以及多线程同步问题有了更为深入的理解。但同时在这次设计过程中也存在着一定的不足,例如没有在调度算法方面进行进一步的优化。但通过这几次作业,也获取到了不少的知识。

猜你喜欢

转载自www.cnblogs.com/1806lay/p/12724546.html