OO第二单元——电梯调度——总结

    OO第二单元总结

    一、设计策略

  三次作业的设计结构都采用的是生产者——消费者结构,即Input输入作为生产者,Elevator电梯作为消费者,中间采用Controller作为共享对象的模式。

    1.线程类

    Elevator:负责运送乘客,主要含有电梯本身的一些参数和Queuein对象,用来存储现在电梯内部所有的Person。

    Input:负责IO处理,读入请求并进行对应处理,在第三次作业中着重处理方面,进行了打表式处理。

         2.资源类

         Controller:作为两类线程的共享对象,分别执行put和get操作对Person进行处理。

    Building:将整幢建筑视为整体,内部装有Floor类用来记录每一层的请求队列,将整个等待队列细分化处理。

    Scheduler:第三次新增类,专门用来执行换乘操作。

  二、分析总结第三次作业架构

  1.SOLID原则

   

字母 指代 概念
S 单一功能原则 认为对象应该仅具有一种单一功能的概念。
O 开闭原则 认为“软件体应该是对于扩展开放的,但是对于修改封闭的”的概念。
L 里氏替换原则 认为“程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的”的概念。参考契约式设计。
I 接口隔离原则 认为“多个特定客户端接口要好于一个宽泛用途的接口”的概念。
D 依赖反转原则 认为一个方法应该遵从“依赖于抽象而不是一个实例”的概念。 依赖注入是该原则的一种实现方式。

    下面将依照SOLID原则对程序从功能设计和性能设计的方面分析和总结第三次作业的可扩展性。

     2.功能设计

     实现基本功能在本单元不是一件特别困难的事情,分别由单电悌过渡到多电梯再过渡到最后可换乘的多电梯,我的功能主要分为几个方面:第一个是输入,由于电梯可达的楼层固定性质,我选择了打表的形式,基于O原则,直接在Input类中进行了操作。第二个是电梯的改变,基本功能都没有做太大的变化,只针对每一次的不同需求作出一些细小的变化,如最大乘客人数和电梯的号码等作出限制和输出的变化。第三个是换乘的实现。由于我的设计没有使用全局队列,每一部电梯是由单独的controller进行控制,相对是独立的,因此换乘操作涉及到了线程之间的沟通,由于需要遵守L遵守,不能进行直接沟通,因此设计了Scheduler专门用于换乘。在Input打表进行判断时设置了boolean变量判断Person是否需要换乘以及换乘哪部电梯,在Person出电梯时进行判断是否需要换乘,进而使用Scheduler类进行Send操作放到指定的请求队列里。

     3.性能设计

     第一个可以优化的点是从静态分配转为动态分配。由于楼层的固定很自然的想到了打表操作,但是如果楼层可达不固定,则需要通过判断各电梯的载人情况和方向、距离等,作出合理的选择put,但这样的操作势必会产生线程间的交互,可能会产生更多线程安全的问题,故为了求稳最后选择了平均的模式,但如果需要性能优化时,还是会选择贪心算法进行选择。

          第二个可以优化的点是电梯运送乘客的策略。本次作业的性能分较为平滑,使用纯look和sstf等算法的最终得分区别并不大,但第三次作业由于可达楼层的限制,比如有A类电梯只能到-3~-1、15~20层这类情况,在调度的过程中可以适当选择短程运输,避免直上直下导致的时间浪费。

   

  三、基于度量分析程序结构

  1.第一次作业

  类图:

  度量分析:

Building.Building() 1.0 2.0 2.0
Building.downisempty(int) 1.0 2.0 3.0
Building.getfloors() 1.0 1.0 1.0
Building.isempty() 1.0 2.0 3.0
Building.upisempty(int) 1.0 2.0 3.0
Controller.Controller() 1.0 1.0 1.0
Controller.get(int,int) 3.0 3.0 3.0
Controller.getBuilding() 1.0 1.0 1.0
Controller.getInputstop() 1.0 1.0 1.0
Controller.put(Person) 1.0 1.0 1.0
Controller.setInputstop() 1.0 1.0 1.0
Elevator.arrive() 1.0 1.0 1.0
Elevator.checkdirection() 1.0 10.0 18.0
Elevator.checkifneedopen() 6.0 13.0 13.0
Elevator.close() 1.0 1.0 1.0
Elevator.Elevator(Controller) 1.0 1.0 1.0
Elevator.getDirection() 1.0 1.0 1.0
Elevator.getNowfloor() 1.0 1.0 1.0
Elevator.in() 2.0 2.0 3.0
Elevator.move() 1.0 6.0 7.0
Elevator.open() 1.0 1.0 1.0
Elevator.out() 1.0 1.0 1.0
Elevator.run() 3.0 3.0 4.0
Elevator.sleep(int) 1.0 2.0 2.0
Floor.add(Person) 1.0 3.0 3.0
Floor.downisempty() 1.0 1.0 1.0
Floor.Floor() 1.0 1.0 1.0
Floor.isempty() 1.0 2.0 2.0
Floor.pushdown() 1.0 2.0 2.0
Floor.pushup() 1.0 2.0 2.0
Floor.upisempty() 1.0 1.0 1.0
Input.Input(Controller) 1.0 1.0 1.0
Input.run() 3.0 4.0 4.0
Main.main(String[]) 1.0 2.0 2.0
Person.getDirection() 1.0 1.0 1.0
Person.getRequest() 1.0 1.0 1.0
Person.Person(PersonRequest) 1.0 2.0 3.0
Queuein.gettofloors() 1.0 1.0 1.0
Queuein.isempty() 1.0 2.0 3.0
Queuein.Queuein() 1.0 2.0 2.0
Tofloor.add(Person) 1.0 1.0 1.0
Tofloor.isempty() 1.0 1.0 1.0
Tofloor.push() 1.0 2.0 2.0
Tofloor.Tofloor() 1.0 1.0 1.0
Total 56.0 93.0 109.0
Average 1.2727272727272727 2.1136363636363638 2.477272727272727

     时序图:

   

    
          可以看到第一次作业除Elevator判断方向和开门方法之外度量做的都比较好,由于判断方向和开门方法中是look算法因此耦合度也不可避免的显得很高。第一次作业模拟的就是纯look电梯,时序图也是最普通的消费者——生产者模型。

  2.第二次作业

       类图:

     度量分析:

   

Building.Building() 1.0 2.0 2.0
Building.downisempty(int) 1.0 2.0 3.0
Building.getFloors() 1.0 1.0 1.0
Building.isempty() 1.0 2.0 3.0
Building.upisempty(int) 1.0 2.0 3.0
Controller.Controller() 1.0 1.0 1.0
Controller.get(int,int,int) 3.0 3.0 3.0
Controller.getBuilding() 1.0 1.0 1.0
Controller.getInputstop() 1.0 1.0 1.0
Controller.put(Person) 1.0 1.0 1.0
Controller.relation(int) 3.0 1.0 5.0
Controller.setInputstop() 1.0 1.0 1.0
Elevator.arrive() 1.0 1.0 1.0
Elevator.checkdirection() 1.0 10.0 18.0
Elevator.checkifneedopen() 6.0 18.0 18.0
Elevator.close() 1.0 1.0 1.0
Elevator.Elevator(Controller,char) 1.0 1.0 1.0
Elevator.getDirection() 1.0 1.0 1.0
Elevator.getNowfloor() 1.0 1.0 1.0
Elevator.in() 2.0 3.0 3.0
Elevator.move() 1.0 6.0 9.0
Elevator.open() 1.0 1.0 1.0
Elevator.out() 1.0 1.0 1.0
Elevator.relation(int) 3.0 1.0 5.0
Elevator.run() 3.0 3.0 4.0
Elevator.sleep(int) 1.0 2.0 2.0
Floor.add(Person) 1.0 3.0 3.0
Floor.downisempty() 1.0 1.0 1.0
Floor.Floor(int) 1.0 1.0 1.0
Floor.getid() 1.0 1.0 1.0
Floor.isempty() 1.0 2.0 2.0
Floor.pushdown(int) 1.0 5.0 5.0
Floor.pushup(int) 1.0 5.0 5.0
Floor.upisempty() 1.0 1.0 1.0
Input.Input() 1.0 1.0 1.0
Input.run() 3.0 6.0 6.0
Main.main(String[]) 1.0 2.0 2.0
Person.getDirection() 1.0 1.0 1.0
Person.getRequest() 1.0 1.0 1.0
Person.Person(PersonRequest) 1.0 2.0 3.0
Queuein.gettofloors() 1.0 1.0 1.0
Queuein.isempty() 1.0 2.0 3.0
Queuein.Queuein() 1.0 2.0 2.0
ToFloor.add(Person) 1.0 1.0 1.0
ToFloor.getTofloorqueue() 1.0 1.0 1.0
ToFloor.isempty() 1.0 1.0 1.0
ToFloor.push(char) 1.0 2.0 2.0
ToFloor.ToFloor() 1.0 1.0 1.0
Total 64.0 111.0 136.0
Average 1.3333333333333333 2.3125 2.8333333333333335

     时序图:

    

   时序图的A、B、C仅为多个电梯的代表,由于电梯的类别一致,分配方法是平均,分别由不同的controller作为共享对象进行调度。

  3.第三次作业

  类图:

     度量分析:

Building.Building() 1.0 2.0 2.0
Building.downisempty(int) 1.0 2.0 3.0
Building.getFloors() 1.0 1.0 1.0
Building.isempty() 1.0 2.0 3.0
Building.upisempty(int) 1.0 2.0 3.0
Controller.Controller() 1.0 1.0 1.0
Controller.get(int,int,int) 3.0 3.0 3.0
Controller.getBuilding() 1.0 1.0 1.0
Controller.getInputstop() 1.0 1.0 1.0
Controller.getOutputstop() 1.0 1.0 1.0
Controller.put(Person,int) 1.0 2.0 2.0
Controller.relation(int) 3.0 1.0 5.0
Controller.setInputstop() 1.0 1.0 1.0
Controller.setOutputstop(boolean) 1.0 1.0 1.0
Elevator.arrive() 1.0 1.0 1.0
Elevator.checkdirection() 1.0 10.0 18.0
Elevator.checkifneedopen() 6.0 18.0 18.0
Elevator.close() 1.0 1.0 1.0
Elevator.Elevator(Scheduler,Controller,String,int,int) 1.0 1.0 1.0
Elevator.getDirection() 1.0 1.0 1.0
Elevator.getNowfloor() 1.0 1.0 1.0
Elevator.in() 2.0 4.0 4.0
Elevator.move() 1.0 5.0 7.0
Elevator.open() 1.0 1.0 1.0
Elevator.out() 1.0 3.0 3.0
Elevator.relation(int) 3.0 1.0 5.0
Elevator.run() 3.0 5.0 6.0
Elevator.sleep(int) 1.0 2.0 2.0
Floor.add(Person) 1.0 3.0 3.0
Floor.downisempty() 1.0 1.0 1.0
Floor.Floor() 1.0 1.0 1.0
Floor.isempty() 1.0 2.0 2.0
Floor.pushdown(int) 1.0 5.0 5.0
Floor.pushup(int) 1.0 5.0 5.0
Floor.upisempty() 1.0 1.0 1.0
Input.distribution(Person) 1.0 24.0 24.0
Input.distribution1(Person,int) 1.0 4.0 8.0
Input.distribution2(Person,int) 1.0 5.0 10.0
Input.distribution3(Person,int) 1.0 12.0 15.0
Input.distribution4(Person,int) 1.0 5.0 9.0
Input.distribution5(Person,int) 1.0 8.0 9.0
Input.distribution6(Person,int) 1.0 5.0 11.0
Input.distribution7(Person,int) 1.0 8.0 9.0
Input.distribution8(Person,int) 1.0 5.0 11.0
Input.distribution9(Person,int) 1.0 8.0 9.0
Input.distribution10(Person,int) 1.0 5.0 11.0
Input.distribution11(Person,int) 1.0 8.0 9.0
Input.distribution12(Person,int) 1.0 5.0 11.0
Input.distribution13(Person,int) 1.0 8.0 9.0
Input.distribution14(Person,int) 1.0 5.0 10.0
Input.distribution15(Person,int) 1.0 5.0 8.0
Input.distribution16(Person,int) 1.0 4.0 8.0
Input.distribution17(Person,int) 1.0 4.0 9.0
Input.distribution18(Person,int) 1.0 4.0 9.0
Input.distribution19(Person,int) 1.0 4.0 8.0
Input.distribution20(Person,int) 1.0 4.0 7.0
Input.distributionminus1(Person,int) 1.0 4.0 10.0
Input.distributionminus2(Person,int) 1.0 4.0 10.0
Input.distributionminus3(Person,int) 1.0 4.0 7.0
Input.initA(Scheduler,String) 1.0 1.0 1.0
Input.initB(Scheduler,String) 1.0 1.0 1.0
Input.initC(Scheduler,String) 1.0 1.0 1.0
Input.Input() 1.0 1.0 1.0
Input.putpersonA(Person) 1.0 1.0 1.0
Input.putpersonB(Person) 1.0 1.0 1.0
Input.putpersonC(Person) 1.0 1.0 1.0
Input.run() 3.0 9.0 9.0
Input.setInputstop() 1.0 4.0 4.0
Input.setperson(Person,int,String) 1.0 1.0 1.0
Main.main(String[]) 1.0 2.0 2.0
Person.addroute(String) 1.0 1.0 1.0
Person.getDirection() 1.0 1.0 1.0
Person.getFlag() 1.0 1.0 1.0
Person.getRequest() 1.0 1.0 1.0
Person.getRoute() 1.0 1.0 1.0
Person.getStopfloor() 1.0 1.0 1.0
Person.Person(PersonRequest) 1.0 2.0 3.0
Person.setDirection(int,int) 1.0 1.0 3.0
Person.setFlag(Boolean) 1.0 1.0 1.0
Person.setStopfloor(int) 1.0 1.0 1.0
Queuein.gettofloors() 1.0 1.0 1.0
Queuein.isempty() 1.0 2.0 3.0
Queuein.Queuein() 1.0 2.0 2.0
Scheduler.isempty() 1.0 7.0 10.0
Scheduler.notifyallcontroller() 1.0 4.0 4.0
Scheduler.Scheduler(ArrayList,ArrayList,ArrayList) 1.0 1.0 1.0
Scheduler.send(Person) 1.0 4.0 4.0
ToFloor.add(Person) 1.0 1.0 1.0
ToFloor.getTofloorqueue() 1.0 1.0 1.0
ToFloor.isempty() 1.0 1.0 1.0
ToFloor.push(String) 1.0 3.0 3.0
ToFloor.ToFloor() 1.0 1.0 1.0
Total 108.0 307.0 425.0
Average 1.173913043478261 3.3369565217391304 4.619565217391305

     时序图:

     大体的结构没有作很大的改变,主要是有1.Input类对新增请求的分析,如果是电梯请求,就增加;如果是Person请求就进行分配,分配的耦合度比较高,这也是打表形式的局限性。2.增加了Scheduler类对换乘Person执行换乘操作,换乘内部也执行的是平均操作,将请求put到对应类型电梯的请求队列中进行等待。

  四、分析程序bug

       由于一直采取求稳的战略,在强测中我没有出现bug。

     但在第一次作业的互测中,我出现了CTLE的问题,原因是我没有对Elevator线程进行wait操作,进而导致CPU被占用时间过长,这个bug在中强测中都没有被发现,导致多线程部分的知识误解了整整一周。出错之后结合实验学到的知识重新深入思考了wait和notify的关系,在后续两次作业中进行了改正。

     第三次作业互测中,出现了一次RTLE的问题,但本地测试样例都没有出现问题,直接重交之后也没有出现问题,怀疑是线程安全导致的问题,由于我Scheduler类的设计导致了线程之间进行了沟通交互,也增大了我程序的危险性,即使深入分析设计了锁的结构,但多线程线程安全的问题本就不能100%的根绝,写程序也尽量要遵守SOLID原则,才能降低更降低风险。

  五、分析他人程序bug

       观察其他人代码结构时,发现有一些同学的设计在数据随机时的表现还是很好,但在一些特殊数据上就会出一些问题,虽然多线程单元没有多项式求导单元边界数据那么多,但还是需要特别注意每次作业迭代时的一些小变化,比如有同学的电梯capacity写错了,即使电梯功能其他都没有问题,但一个小错误强测就炸了。

  六、心得体会

  第一单元我有一次无效作业,因此在第二单元的学习、写作业时也格外的认真,学到了不少。多线程是一个非常有趣的东西,它一方面带来了效率和交互的方便,另一方面又带来了可怕的线程安全问题。也希望自己在每一单元的第一次作业尽快进入状态,同时要思考程序的可拓展性,节省时间,提高效率。

  

 

posted @ 2020-04-17 15:56  BH-18373176  阅读(201)  评论(0编辑  收藏  举报