面向对象第二次作业总结-电梯调度
OO第二次作业总结-电梯调度
一.作业回顾
-
第一次作业:支持FCFS的一台电梯调度
-
第二次作业:支持ALS的一台电梯调度
-
第三次作业:支持协同调度的多台电梯调度
二.第一次作业
2.1 设计思路
本次作业采用三线程,即输入线程,调度线程,电梯线程。每两个线程之间使用一个队列容器作为托盘,实际上是两个生产者-消费者模式,该架构思路即为电梯只负责抓取指令并且运行,而调度器负责给电梯增添指令。
2.2 UML类图与线程运行逻辑与时序图
在本次作业中我的主线程只负责创建线程与实例化对象,只是一个操作线程,而没有实际作用,不予介绍。
-
Input线程
运行逻辑:不停从系统输入中获取输入,并且
add
到queueForInput
中去。死亡条件:当输入为
null
时结束。1 public void run() { 2 //TimableOutput.println("Running" + threadName); 3 ElevatorInput elevatorInput = new ElevatorInput(System.in); 4 while (true) { 5 PersonRequest request = elevatorInput.nextPersonRequest(); 6 if (request == null) { 7 //down = false; 8 break; 9 } else { 10 //TimableOutput.println( 11 // "add " + request.toString() + "to Input queue"); 12 queueOfInput.add(request); 13 } 14 } 15 try { 16 elevatorInput.close(); 17 } 18 catch (Exception e) { 19 TimableOutput.println("Fucked " + threadName); 20 } 21 }
-
Scheduler线程
运行逻辑:不断地从
queueOfInput
中poll
(其中的wait()
写在了poll
函数里),并且add
到queueOfElevator
中去。死亡条件:当
input
线程已结束,并且queueOfInput
为空。1 public void run() { 2 //TimableOutput.println("Running" + threadName); 3 PersonRequest request; 4 while (input.isDown() || queueOfInput.getQueueSize() != 0) { 5 if (queueOfInput.getQueueSize() != 0) { 6 synchronized (this) { 7 request = queueOfInput.poll(); 8 queueOfElevator.add(request); 9 } 10 } 11 } 12 }
-
Elevator线程
运行逻辑:不断地从
queueOfElevator
中poll
(其中的wait()
写在了poll
函数里),并且直接对poll
到的指令进行操作。死亡条件:当
input
线程与scheduler
线程已结束,并且queueOfInput
与queueOfElevator
为空。1 public void run() { 2 //TimableOutput.println("Running " + threadName); 3 PersonRequest request; 4 while (scheduler.isOK()) { 5 if (queueOfElevator.getQueueSize() != 0) { 6 request = queueOfElevator.poll(); 7 getTo(request.getFromFloor()); 8 openDoor(request.getFromFloor()); 9 peopleIn(request.getPersonId(),request.getFromFloor()); 10 closeDoor(request.getFromFloor()); 11 getTo(request.getToFloor()); 12 openDoor(request.getToFloor()); 13 peopleOut(request.getPersonId(),request.getToFloor()); 14 closeDoor(request.getToFloor()); 15 } 16 } 17 }
时序图:
时序图可以看出三个线程都没有wait(),而是在轮询,输入线程在结束之前告知调度线程,调度线程在结束之前告知电梯线程。
2.3 同步控制实现
由于是一个简单的生产者消费者模型,故而只需要在容器类的poll
与add
函数中实现同步控制即可。但是本次的代码并没有让线程进入到wait()函数,实际上是一个轮询式的操作。
本次同步控制的实现一大重点时对于线程运行的控制,用我简单的话来说就是让线程在不应该结束时绝不结束,在线程应该结束时一定结束。其本质是对线程结束控制的信号进行管理。
比如input
线程结束是使用了输入为null
的信号。
而scheduler
线程结束是使用了input
线程已结束,并且queueOfInput
为空为信号。
而elevator
线程比较有意思,最初我定的结束条件为输入线程结束,并且两个队列为空,当时我认为此条件等同于当前以及未来都没有可处理任务。但是这个等同不是双向的,有一种状况,当输入线程结束,一条指令从queueOfInput
被调度线程poll
出来,还没有add
到queueOfElevator
中时,满足该条件,电梯线程结束,有一条指令未处理。而在这个叙述中很容易看出需要将shceduler
线程的存活信号加入到判断条件中去。
从本次的同步控制实现中,我明白线程间的控制,尤其时线程结束的控制一定要结合线程的运行过程,对运行过程仔细思考来决定其结束条件,否则将会出现线程在出乎意料的时刻结束的痛苦(指debug)情况。
2.4 度量分析
类表:
Project Name | Package Name | Type Name | NOF | NOPF | NOM | NOPM | LOC | WMC | NC | DIT | LCOM | FANIN | FANOUT |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
homework_5 | elevator | Elevator | 5 | 0 | 8 | 2 | 71 | 11 | 0 | 0 | 0 | 1 | 2 |
homework_5 | elevator | Input | 3 | 0 | 4 | 3 | 36 | 7 | 0 | 0 | 0 | 2 | 1 |
homework_5 | elevator | MainClass | 0 | 0 | 1 | 1 | 15 | 1 | 0 | 0 | -1 | 0 | 5 |
homework_5 | elevator | QueueFor | 2 | 0 | 6 | 5 | 40 | 7 | 2 | 0 | 0 | 0 | 0 |
homework_5 | elevator | QueueOfElevator | 0 | 0 | 3 | 2 | 9 | 3 | 0 | 1 | -1 | 3 | 0 |
homework_5 | elevator | QueueOfInput | 0 | 0 | 1 | 0 | 5 | 1 | 0 | 1 | -1 | 3 | 0 |
homework_5 | elevator | Scheduler | 5 | 0 | 4 | 3 | 33 | 7 | 0 | 0 | 0 | 2 | 3 |
其中NOF (Number Of Fields)代表域个数,NOM (Number Of Methods)代表方法个数,NOPM (Number of Public Methods)代表公共方法个数,LOC (Lines of Code)代表类总代码规模(行数)。
方法表:
Project Name | Package Name | Type Name | MethodName | LOC | CC | PC |
---|---|---|---|---|---|---|
homework_5 | elevator | Elevator | Elevator | 6 | 1 | 3 |
homework_5 | elevator | Elevator | Elevator | 16 | 3 | 0 |
homework_5 | elevator | Elevator | Elevator | 12 | 1 | 1 |
homework_5 | elevator | Elevator | Elevator | 9 | 1 | 1 |
homework_5 | elevator | Elevator | Elevator | 9 | 1 | 1 |
homework_5 | elevator | Elevator | Elevator | 3 | 1 | 2 |
homework_5 | elevator | Elevator | Elevator | 3 | 1 | 2 |
homework_5 | elevator | Elevator | Elevator | 6 | 2 | 0 |
homework_5 | elevator | Input | Input | 4 | 1 | 2 |
homework_5 | elevator | Input | Input | 3 | 1 | 0 |
homework_5 | elevator | Input | Input | 18 | 3 | 0 |
homework_5 | elevator | Input | Input | 6 | 2 | 0 |
homework_5 | elevator | MainClass | MainClass | 13 | 1 | 1 |
homework_5 | elevator | QueueFor | QueueFor | 3 | 1 | 0 |
homework_5 | elevator | QueueFor | QueueFor | 8 | 1 | 1 |
homework_5 | elevator | QueueFor | QueueFor | 18 | 2 | 0 |
homework_5 | elevator | QueueFor | QueueFor | 3 | 1 | 0 |
homework_5 | elevator | QueueFor | QueueFor | 2 | 1 | 1 |
homework_5 | elevator | QueueFor | QueueFor | 2 | 1 | 1 |
homework_5 | elevator | QueueOfElevator | QueueOfElevator | 3 | 1 | 0 |
homework_5 | elevator | QueueOfElevator | QueueOfElevator | 2 | 1 | 1 |
homework_5 | elevator | QueueOfElevator | QueueOfElevator | 2 | 1 | 1 |
homework_5 | elevator | QueueOfInput | QueueOfInput | 3 | 1 | 0 |
homework_5 | elevator | Scheduler | Scheduler | 6 | 1 | 4 |
homework_5 | elevator | Scheduler | Scheduler | 11 | 3 | 0 |
homework_5 | elevator | Scheduler | Scheduler | 3 | 1 | 0 |
homework_5 | elevator | Scheduler | Scheduler | 6 | 2 | 0 |
分析程序有一些bug无法看到方法名,但是每个类的长度都不长。
度量分析表:
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
elevator.Elevator.Elevator(String,Scheduler,QueueOfElevator) | 1 | 1 | 1 |
elevator.Elevator.closeDoor(int) | 1 | 2 | 2 |
elevator.Elevator.getTo(int) | 1 | 2 | 2 |
elevator.Elevator.openDoor(int) | 1 | 2 | 2 |
elevator.Elevator.peopleIn(int,int) | 1 | 1 | 1 |
elevator.Elevator.peopleOut(int,int) | 1 | 1 | 1 |
elevator.Elevator.run() | 1 | 3 | 3 |
elevator.Elevator.start() | 1 | 2 | 2 |
elevator.Input.Input(String,QueueOfInput) | 1 | 1 | 1 |
elevator.Input.isDown() | 1 | 1 | 1 |
elevator.Input.run() | 3 | 4 | 4 |
elevator.Input.start() | 1 | 2 | 2 |
elevator.MainClass.main(String[]) | 1 | 1 | 1 |
elevator.QueueFor.QueueFor() | 1 | 1 | 1 |
elevator.QueueFor.add(PersonRequest) | 1 | 1 | 1 |
elevator.QueueFor.getQueueSize() | 1 | 1 | 1 |
elevator.QueueFor.poll() | 1 | 3 | 3 |
elevator.QueueFor.printAdd(PersonRequest) | 1 | 1 | 1 |
elevator.QueueFor.printPoll(PersonRequest) | 1 | 1 | 1 |
elevator.QueueOfElevator.QueueOfElevator() | 1 | 1 | 1 |
elevator.QueueOfElevator.printAdd(PersonRequest) | 1 | 1 | 1 |
elevator.QueueOfElevator.printPoll(PersonRequest) | 1 | 1 | 1 |
elevator.QueueOfInput.QueueOfInput() | 1 | 1 | 1 |
elevator.Scheduler.Scheduler(String,QueueOfInput,QueueOfElevator,Input) | 1 | 1 | 1 |
elevator.Scheduler.isOK() | 1 | 4 | 4 |
elevator.Scheduler.run() | 1 | 4 | 4 |
elevator.Scheduler.start() | 1 | 2 | 2 |
Class | OCavg | WMC | |
elevator.Elevator | 1.38 | 11 | |
elevator.Input | 1.75 | 7 | |
elevator.MainClass | 1 | 1 | |
elevator.QueueFor | 1.17 | 7 | |
elevator.QueueOfElevator | 1 | 3 | |
elevator.QueueOfInput | 1 | 1 | |
elevator.Scheduler | 1.75 | 7 | |
Package | v(G)avg | v(G)tot | |
elevator | 1.7 | 46 | |
Module | v(G)avg | v(G)tot | |
homework_5 | 1.7 | 46 | |
Project | v(G)avg | v(G)tot | |
project | 1.7 | 46 |
从表中可以看到本次作业的基本复杂度,模块设计复杂度和求导复杂度都非常正常,说明代码的模块设计以及类的功能设计划分,方法的功能设计划分合理,基本符合高内聚低耦合的设计要求。
2.5 程序评价
不只是这一次,后续的两次作业由于对多线程的恐惧,都没有实现指导书中说的调度线程与电梯线程之间的高强度交互,意即彻底将电梯与调度器的功能分解开,电梯真正的只负责运行调度器分发的指令,而调度器真正掌控全部的调度能力。
并且本次作业没有使用wait()与notifyAll(),虽然实现了但是没有使用。即三个线程都采用轮询,非常消耗CPU资源。
另外有一点不知道是否是优点或是缺点,即将线程之间的通信基本通过容器来进行,这样可以让线程之间彼此降低干扰,降低线程运行控制难度,但是增加了通信的代价,即将两个线程之间联通了另一个类来传送信息,同时由于这样不能很全面的获知另一个类中的信息,比如调度器无法获知电梯现在的状态,从而无法进行精准调度。
但其实本次作业还是对其做出了尝试,比如传递了线程的死亡信号,但是对于高频率的交互,仍然是望而却步了。
对程序进行SOLID 原则分析
SRP:每个类的职责都很明确
OCP:不满足,在本次作业中很多地方都使用了硬编码来简化代码结构,除了整体架构之外很多地方的扩展性较差
LSP:满足,在出现父类的时候,都可以用子类进行替换。
ISP:未使用接口
DIP:符合,高层次模块依赖于依赖于低层次的抽象。
总体来说这是一次较成功的作业。
三.第二次作业
3.1 设计思路
本次作业仍然采用三线程,即输入线程,调度线程,电梯线程。每两个线程之间使用一个队列容器作为托盘,但是本次将后一个容器改为了hashMap
,使用楼层作为key
,使用以指令为元素的arrayList
作为value
,为电梯的捎带操作提供便利,本次作业调度器线程负责将指令塞到等待楼层并根据其上下行,塞到上行或者下行的hashMap
中,而电梯则在其为空时寻找一个最近的指令作为主指令,接到主指令之后以主指令运行方向,捎带过程中遇到的同向乘客,直到电梯为空,寻找下一个主指令。
3.2 UML类图与线程运行逻辑与时序图
在本次作业中我的主线程只负责创建线程与实例化对象,只是一个操作线程,而没有实际作用,不予介绍。
-
Input线程
运行逻辑:不停从系统输入中获取输入,并且
add
到queueForInput
中去。死亡条件:当输入为
null
时结束。1 public void run() { 2 //TimableOutput.println("Running" + threadName); 3 ElevatorInput elevatorInput = new ElevatorInput(System.in); 4 while (true) { 5 PersonRequest request = elevatorInput.nextPersonRequest(); 6 if (request == null) { 7 //down = false; 8 break; 9 } else { 10 //TimableOutput.println( 11 // "add " + request.toString() + "to Input queue"); 12 queueOfInput.add(request); 13 } 14 } 15 //TimableOutput.println("Fucked " + threadName); 16 queueOfInput.awakeAndGoDie(); 17 try { 18 elevatorInput.close(); 19 } 20 catch (Exception e) { 21 TimableOutput.println("Fucked " + threadName); 22 } 23 }
-
Scheduler线程
运行逻辑:不断地从
queueOfInput
中poll
(其中的wait()
写在了poll
函数里),并且根据其时上行还是下行add
到hashMapOfElevator
中上行hashMap
还是下行hashMap
中去。死亡条件:当
input
线程已结束,并且queueOfInput
为空。1 public void run() { 2 //TimableOutput.println("Running" + threadName); 3 PersonRequest request; 4 while (input.isDown() || queueOfInput.getQueueSize() != 0) { 5 //TimableOutput.println("sc not die"); 6 request = queueOfInput.poll(); 7 if (request == null) { 8 break; 9 } 10 if (request.getFromFloor() < request.getToFloor()) { 11 hashMapOfElevator.addUp(request); 12 } else { 13 hashMapOfElevator.addDown(request); 14 } 15 } 16 //TimableOutput.println("scheduler died"); 17 hashMapOfElevator.awake(); 18 }
-
Elevator线程
运行逻辑:在其为空时寻找一个最近楼层的指令作为主指令,接到主指令之后以主指令运行方向,过程中取出同层的同向等待指令,直到电梯为空,寻找下一个主指令。
死亡条件:当
input
线程与scheduler
线程已结束,并且queueOfInput
与hashMapOfElevator
为空。1 public synchronized void run() { 2 //TimableOutput.println("Running " + threadName); 3 PersonRequest request; 4 while (scheduler.isOK()) { 5 //TimableOutput.println("elevator waiting"); 6 queueOfElevator.sleepWaiting(); 7 //TimableOutput.println("elevator awake"); 8 if (queueOfElevator.getDieAndAway() 9 && queueOfElevator.getHashSizeDown() == 0 10 && queueOfElevator.getHashSizeUp() == 0) { 11 break; 12 } 13 find(); 14 while (peopleInElevator.size() == 0) { 15 ArrayList<PersonRequest> tempArrayList; 16 if (inUpOrDown) { 17 tempArrayList = queueOfElevator.pollUp(floorNow); 18 if (tempArrayList == null) { 19 upOrDown(); 20 find(); 21 continue; 22 } 23 else { 24 openDoor(floorNow); 25 for (int i = 0;i < tempArrayList.size();i++) { 26 peopleInElevator.add(tempArrayList.get(i)); 27 peopleIn(tempArrayList.get(i).getPersonId(), 28 tempArrayList.get(i).getFromFloor()); 29 } 30 closeDoor(floorNow); 31 upOrDown = inUpOrDown; 32 } 33 } 34 else { 35 tempArrayList = queueOfElevator.pollDown(floorNow); 36 if (tempArrayList == null) { 37 upOrDown(); 38 find(); 39 continue; 40 } 41 else { 42 openDoor(floorNow); 43 for (int i = 0;i < tempArrayList.size();i++) { 44 peopleInElevator.add(tempArrayList.get(i)); 45 peopleIn(tempArrayList.get(i).getPersonId(), 46 tempArrayList.get(i).getFromFloor()); 47 } 48 closeDoor(floorNow); 49 upOrDown = inUpOrDown; 50 } 51 } 52 upOrDown(); 53 //find(); 54 } 55 operate(); 56 } 57 }
1 private void operate() { 2 while (peopleInElevator.size() != 0) { 3 openDoor = false; 4 someoneDestinates(); 5 if (peopleInElevator.size() == 0) 6 { 7 if (openDoor) { 8 closeDoor(floorNow); 9 } 10 TimableOutput.println("one turn is over"); 11 break; 12 } 13 ArrayList<PersonRequest> tempArrayList; 14 //TimableOutput.println("here1"); 15 if (upOrDown) { 16 tempArrayList = queueOfElevator.pollUp(floorNow); 17 if (tempArrayList == null) { 18 //TimableOutput.println("no in people"); 19 if (openDoor) { 20 closeDoor(floorNow); 21 } 22 upOrDown(); 23 continue; 24 } 25 else { 26 //TimableOutput.println(" in people"); 27 if (!openDoor) { 28 openDoor(floorNow); 29 } 30 for (int i = 0;i < tempArrayList.size();i++) { 31 peopleInElevator.add(tempArrayList.get(i)); 32 peopleIn(tempArrayList.get(i).getPersonId(), 33 tempArrayList.get(i).getFromFloor()); 34 } 35 closeDoor(floorNow); 36 } 37 //TimableOutput.println("fuck"); 38 } 39 else { 40 tempArrayList = queueOfElevator.pollDown(floorNow); 41 if (tempArrayList == null) { 42 //TimableOutput.println("no in people"); 43 if (openDoor) { 44 closeDoor(floorNow); 45 } 46 upOrDown(); 47 continue; 48 } 49 else { 50 if (!openDoor) { 51 openDoor(floorNow); 52 } 53 for (int i = 0;i < tempArrayList.size();i++) { 54 peopleInElevator.add(tempArrayList.get(i)); 55 peopleIn(tempArrayList.get(i).getPersonId(), 56 tempArrayList.get(i).getFromFloor()); 57 } 58 closeDoor(floorNow); 59 } 60 } 61 upOrDown(); 62 } 63 }
时序图:
时序图看到,除了输出线程在轮询,调度线程在poll时如果队列为空会等待,而电梯线程在准备运行时,如果hashMap为空会等待,而输入线程add之后会唤醒调度线程,调度线程add后会唤醒电梯线程。电梯线程结束前告知调度线程,同时修改信号,调度线程接受信号跳出循环,告知电梯线程,修改信号结束,电梯线程收到信号跳出循环结束。
3.3 同步控制实现
本次的同步控制完全由synchronized
与wait()
,notifyAll()
实现,即在调度器调用poll
时如果队列为空则会wait()
,而输入线程add
时则会notifyAll
,同时为了避免调度器在wait
时,输入线程结束而导致其永久的wait
,在队列中有一个调度器是否能结束信号,输入线程在结束之前会先将其置true
同时notifyAll
,而poll
则会在被唤醒时判断本信号为true返回null
从而让调度器线程结束。此操作在电梯线程上同理。
本次作业实现了真正的同步控制,而非让线程处于轮询状态。
本次作业在思考如何让线程最大限度上的wait(),以及在需要被唤醒,和结束的时候正确运行上花了很多功夫。最终有所成果,并且对多线程的控制有所体会。
3.4 度量分析
类表:
Project Name | Package Name | Type Name | NOF | NOPF | NOM | NOPM | LOC | WMC | NC | DIT | LCOM | FANIN | FANOUT |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
hmwk_6 | elevator | Elevator | 10 | 0 | 14 | 2 | 255 | 52 | 0 | 0 | 0 | 1 | 2 |
hmwk_6 | elevator | HashMapOfElevator | 5 | 0 | 12 | 11 | 101 | 18 | 0 | 0 | 0 | 3 | 0 |
hmwk_6 | elevator | Input | 3 | 0 | 4 | 3 | 37 | 7 | 0 | 0 | 0 | 2 | 1 |
hmwk_6 | elevator | MainClass | 0 | 0 | 1 | 1 | 15 | 1 | 0 | 0 | -1 | 0 | 5 |
hmwk_6 | elevator | QueueFor | 3 | 0 | 7 | 6 | 48 | 9 | 1 | 0 | 0.428571 | 0 | 0 |
hmwk_6 | elevator | QueueOfInput | 0 | 0 | 1 | 0 | 5 | 1 | 0 | 1 | -1 | 3 | 0 |
hmwk_6 | elevator | Scheduler | 5 | 0 | 4 | 3 | 38 | 8 | 0 | 0 | 0 | 2 | 3 |
其中NOF (Number Of Fields)代表域个数,NOM (Number Of Methods)代表方法个数,NOPM (Number of Public Methods)代表公共方法个数,LOC (Lines of Code)代表类总代码规模(行数)。
可以看到Elevator线程的行数极大,其主要原因是没有将电梯的运行逻辑最大的简化与分离,将其分配给调度器。但其实我觉得本次我写的电梯运行逻辑,应该才是一部正常电梯应当具有的基本逻辑。
方法表:
Project Name | Package Name | Type Name | MethodName | LOC | CC | PC |
---|---|---|---|---|---|---|
hmwk_6 | elevator | Elevator | Elevator | 6 | 1 | 3 |
hmwk_6 | elevator | Elevator | Elevator | 49 | 9 | 0 |
hmwk_6 | elevator | Elevator | Elevator | 55 | 13 | 0 |
hmwk_6 | elevator | Elevator | Elevator | 8 | 2 | 0 |
hmwk_6 | elevator | Elevator | Elevator | 23 | 6 | 0 |
hmwk_6 | elevator | Elevator | Elevator | 28 | 6 | 0 |
hmwk_6 | elevator | Elevator | Elevator | 16 | 2 | 0 |
hmwk_6 | elevator | Elevator | Elevator | 16 | 2 | 0 |
hmwk_6 | elevator | Elevator | Elevator | 12 | 5 | 1 |
hmwk_6 | elevator | Elevator | Elevator | 9 | 1 | 1 |
hmwk_6 | elevator | Elevator | Elevator | 9 | 1 | 1 |
hmwk_6 | elevator | Elevator | Elevator | 3 | 1 | 2 |
hmwk_6 | elevator | Elevator | Elevator | 3 | 1 | 2 |
hmwk_6 | elevator | Elevator | Elevator | 6 | 2 | 0 |
hmwk_6 | elevator | HashMapOfElevator | HashMapOfElevator | 4 | 1 | 0 |
hmwk_6 | elevator | HashMapOfElevator | HashMapOfElevator | 17 | 2 | 1 |
hmwk_6 | elevator | HashMapOfElevator | HashMapOfElevator | 12 | 2 | 1 |
hmwk_6 | elevator | HashMapOfElevator | HashMapOfElevator | 17 | 2 | 1 |
hmwk_6 | elevator | HashMapOfElevator | HashMapOfElevator | 12 | 2 | 1 |
hmwk_6 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 0 |
hmwk_6 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 0 |
hmwk_6 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 1 |
hmwk_6 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 1 |
hmwk_6 | elevator | HashMapOfElevator | HashMapOfElevator | 13 | 3 | 0 |
hmwk_6 | elevator | HashMapOfElevator | HashMapOfElevator | 4 | 1 | 0 |
hmwk_6 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 0 |
hmwk_6 | elevator | Input | Input | 4 | 1 | 2 |
hmwk_6 | elevator | Input | Input | 3 | 1 | 0 |
hmwk_6 | elevator | Input | Input | 19 | 3 | 0 |
hmwk_6 | elevator | Input | Input | 6 | 2 | 0 |
hmwk_6 | elevator | MainClass | MainClass | 13 | 1 | 1 |
hmwk_6 | elevator | QueueFor | QueueFor | 3 | 1 | 0 |
hmwk_6 | elevator | QueueFor | QueueFor | 7 | 1 | 1 |
hmwk_6 | elevator | QueueFor | QueueFor | 20 | 3 | 0 |
hmwk_6 | elevator | QueueFor | QueueFor | 3 | 1 | 0 |
hmwk_6 | elevator | QueueFor | QueueFor | 3 | 1 | 1 |
hmwk_6 | elevator | QueueFor | QueueFor | 3 | 1 | 1 |
hmwk_6 | elevator | QueueFor | QueueFor | 4 | 1 | 0 |
hmwk_6 | elevator | QueueOfInput | QueueOfInput | 3 | 1 | 0 |
hmwk_6 | elevator | Scheduler | Scheduler | 6 | 1 | 4 |
hmwk_6 | elevator | Scheduler | Scheduler | 16 | 4 | 0 |
hmwk_6 | elevator | Scheduler | Scheduler | 3 | 1 | 0 |
hmwk_6 | elevator | Scheduler | Scheduler | 6 | 2 | 0 |
分析程序有一些bug无法看到方法名,但是每个类的长度都不长。除了Elevator中的两个有关运行逻辑的方法,run()和operate()。
度量分析表:
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
elevator.Elevator.Elevator(String,Scheduler,HashMapOfElevator) | 1 | 1 | 1 |
elevator.Elevator.closeDoor(int) | 1 | 2 | 2 |
elevator.Elevator.down() | 1 | 3 | 3 |
elevator.Elevator.find() | 6 | 5 | 6 |
elevator.Elevator.getTo(int) | 1 | 5 | 5 |
elevator.Elevator.openDoor(int) | 1 | 2 | 2 |
elevator.Elevator.operate() | 6 | 13 | 13 |
elevator.Elevator.peopleIn(int,int) | 1 | 1 | 1 |
elevator.Elevator.peopleOut(int,int) | 1 | 1 | 1 |
elevator.Elevator.run() | 7 | 10 | 11 |
elevator.Elevator.someoneDestinates() | 3 | 5 | 6 |
elevator.Elevator.start() | 1 | 2 | 2 |
elevator.Elevator.up() | 1 | 3 | 3 |
elevator.Elevator.upOrDown() | 1 | 2 | 2 |
elevator.HashMapOfElevator.HashMapOfElevator() | 1 | 1 | 1 |
elevator.HashMapOfElevator.addDown(PersonRequest) | 1 | 2 | 2 |
elevator.HashMapOfElevator.addUp(PersonRequest) | 1 | 2 | 2 |
elevator.HashMapOfElevator.awake() | 1 | 1 | 1 |
elevator.HashMapOfElevator.containDown(int) | 1 | 1 | 1 |
elevator.HashMapOfElevator.containUp(int) | 1 | 1 | 1 |
elevator.HashMapOfElevator.getDieAndAway() | 1 | 1 | 1 |
elevator.HashMapOfElevator.getHashSizeDown() | 1 | 1 | 1 |
elevator.HashMapOfElevator.getHashSizeUp() | 1 | 1 | 1 |
elevator.HashMapOfElevator.pollDown(int) | 2 | 2 | 2 |
elevator.HashMapOfElevator.pollUp(int) | 2 | 2 | 2 |
elevator.HashMapOfElevator.sleepWaiting() | 3 | 3 | 5 |
elevator.Input.Input(String,QueueOfInput) | 1 | 1 | 1 |
elevator.Input.isDown() | 1 | 1 | 1 |
elevator.Input.run() | 3 | 4 | 4 |
elevator.Input.start() | 1 | 2 | 2 |
elevator.MainClass.main(String[]) | 1 | 1 | 1 |
elevator.QueueFor.QueueFor() | 1 | 1 | 1 |
elevator.QueueFor.add(PersonRequest) | 1 | 1 | 1 |
elevator.QueueFor.awakeAndGoDie() | 1 | 1 | 1 |
elevator.QueueFor.getQueueSize() | 1 | 1 | 1 |
elevator.QueueFor.poll() | 3 | 3 | 4 |
elevator.QueueFor.printAdd(PersonRequest) | 1 | 1 | 1 |
elevator.QueueFor.printPoll(PersonRequest) | 1 | 1 | 1 |
elevator.QueueOfInput.QueueOfInput() | 1 | 1 | 1 |
elevator.Scheduler.Scheduler(String,QueueOfInput,HashMapOfElevator,Input) | 1 | 1 | 1 |
elevator.Scheduler.isOK() | 1 | 5 | 5 |
elevator.Scheduler.run() | 3 | 4 | 5 |
elevator.Scheduler.start() | 1 | 2 | 2 |
Class | OCavg | WMC | |
elevator.Elevator | 3.71 | 52 | |
elevator.HashMapOfElevator | 1.5 | 18 | |
elevator.Input | 1.75 | 7 | |
elevator.MainClass | 1 | 1 | |
elevator.QueueFor | 1.29 | 9 | |
elevator.QueueOfInput | 1 | 1 | |
elevator.Scheduler | 2 | 8 | |
Package | v(G)avg | v(G)tot | |
elevator | 2.58 | 111 | |
Module | v(G)avg | v(G)tot | |
hmwk_6 | 2.58 | 111 | |
Project | v(G)avg | v(G)tot | |
project | 2.58 | 111 |
从表中可以看到本次作业的基本复杂度,模块设计复杂度和求导复杂度都非常正常,除了Elevator.find()与另外两个运行逻辑函数,find()较高是由于使用了对hashMap
的便利查找操作,该方法应当写在hashMap
类中,而其余两个运行逻辑函数由于调用了多次find(),并且运行逻辑的判断确实过于复杂,从而导致其复杂度较高。但是整体来说还是符合高内聚,低耦合的。
3.5 程序评价
本次作业基本套用了上次作业的框架,只将轮询改为了wait
与notifyAll
的操作,同时修改了后一个容器类型从而使电梯操作方便,而且完全重写了电梯的运行逻辑,并且对运行逻辑中可优化的地方进行了优化,比如当电梯运送完最后一个乘客后,不再机械性运行一次,当本楼层同时有上下时,不重复开门,但是仍然漏掉了一点,即在本楼层电梯上人下空后,应判断当前楼层有无人上电梯之后再看是否重新寻找主指令。
本次作业还是存在一些小bug,在寻找最近主指令时对于其寻找范围少算了1。从而导致了电梯会无法停止的情况。
同时本次程序的电梯线程的鲁棒性极低,完全由程序结构,以及调度器的正确运行所保证。即没有在电梯线程中写遇到WF的操作。
对程序进行SOLID 原则分析
SRP:每个类的职责都很明确,但是有一个方法位置不对,同时有两个方法过于复杂。
OCP:基本满足。
LSP:没有子类。
ISP:未使用接口
DIP:符合,高层次模块依赖于依赖于低层次的抽象。
四.第三次作业
4.1 设计思路
本次作业仍然采用三线程,即输入线程,调度线程,电梯线程。每两个线程之间使用一个队列容器作为托盘,本次区别是一个调度器将通过三个hashMap
类与三个电梯线程沟通,本次作业调度器线程负责将指令依次(指通过if-else强制按顺序)判断,并塞到单一楼层(即该楼层只有该电梯能到)的电梯,不满且直达(指目标和等待楼层该电梯都能到达)的电梯,不满且能捎带的电梯,直达的电梯(不管是否满员,满员则wait()(关于一个调度器如何与三部电梯同步控制,在后面小节中叙述)),能捎带的电梯,B电梯。
而电梯的运行则是依照第二次作业的运行逻辑,增加了乘客队列,以及对不直达乘客的下电梯判断,指令拆分回扔的内容。
4.2 UML类图与线程运行逻辑与时序图
在本次作业中我的主线程只负责创建线程与实例化对象,只是一个操作线程,而没有实际作用,不予介绍。
-
Input线程
运行逻辑:不停从系统输入中获取输入,并且
add
到queueForInput
中去。死亡条件:当输入为
null
时结束。1 public void run() { 2 //TimableOutput.println("Running" + threadName); 3 ElevatorInput elevatorInput = new ElevatorInput(System.in); 4 while (true) { 5 PersonRequest request = elevatorInput.nextPersonRequest(); 6 if (request == null) { 7 //down = false; 8 break; 9 } else { 10 //TimableOutput.println( 11 // "add " + request.toString() + "to Input queue"); 12 queueOfInput.add(request); 13 } 14 } 15 //TimableOutput.println("Fucked " + threadName); 16 queueOfInput.awakeAndGoDie(); 17 try { 18 elevatorInput.close(); 19 } 20 catch (Exception e) { 21 TimableOutput.println("Fucked " + threadName); 22 } 23 }
-
Scheduler线程
运行逻辑:见4.1
死亡条件:当
input
线程已结束,并且queueOfInput
为空,并且三个电梯线程都没有需要拆分的指令了。1 public void run() { 2 //TimableOutput.println("Running" + threadName); 3 PersonRequest request; 4 while (input.isDown() 5 || hashMapOfElevatorA.getNumOfBadRequest() != 0 6 || hashMapOfElevatorB.getNumOfBadRequest() != 0 7 || hashMapOfElevatorC.getNumOfBadRequest() != 0 8 || queueOfInput.getQueueSize() != 0) { 9 //TimableOutput.println("sc not die"); 10 request = queueOfInput.poll(); 11 if (request == null) { 12 break; 13 } 14 distribute(request); 15 } 16 //TimableOutput.println("scheduler died"); 17 hashMapOfElevatorA.awake(); 18 hashMapOfElevatorB.awake(); 19 hashMapOfElevatorC.awake(); 20 }
-
Elevator线程
运行逻辑:在其为空时寻找一个最近楼层的指令作为主指令,接到主指令之后以主指令运行方向,过程中取出同层的同向等待指令,直到电梯为空,寻找下一个主指令。
死亡条件:当
input
线程与scheduler
线程已结束,并且queueOfInput
与hashMapOfElevator
为空。1 public synchronized void run() { 2 //TimableOutput.println("Running " + threadName); 3 PersonRequest request; 4 while (scheduler.isOK()) { 5 //TimableOutput.println("elevator waiting"); 6 queueOfElevator.sleepWaiting(); 7 //TimableOutput.println("elevator awake"); 8 if (queueOfElevator.getDieAndAway() 9 && queueOfElevator.getHashSizeDown() == 0 10 && queueOfElevator.getHashSizeUp() == 0) { 11 break; 12 } 13 find(); 14 while (peopleInElevator.size() == 0) { 15 ArrayList<PersonRequest> tempArrayList; 16 if (inUpOrDown) { 17 tempArrayList = queueOfElevator.pollUp(floorNow); 18 if (tempArrayList == null) { 19 upOrDown(); 20 find(); 21 continue; 22 } else { 23 openDoor(floorNow); 24 for (int i = 0; i < tempArrayList.size(); i++) { 25 peopleInElevator.add(tempArrayList.get(i)); 26 peopleIn(tempArrayList.get(i)); 27 } 28 closeDoor(floorNow); 29 upOrDown = inUpOrDown; 30 } 31 } else { 32 tempArrayList = queueOfElevator.pollDown(floorNow); 33 if (tempArrayList == null) { 34 upOrDown(); 35 find(); 36 continue; 37 } else { 38 openDoor(floorNow); 39 for (int i = 0; i < tempArrayList.size(); i++) { 40 peopleInElevator.add(tempArrayList.get(i)); 41 peopleIn(tempArrayList.get(i)); 42 } 43 closeDoor(floorNow); 44 upOrDown = inUpOrDown; 45 } 46 } 47 upOrDown(); 48 } 49 operate(); 50 } 51 }
1 private void operate() { 2 while (peopleInElevator.size() != 0) { 3 openDoor = false; 4 someoneDestinates(); 5 if (peopleInElevator.size() == 0) { 6 if (openDoor) { 7 closeDoor(floorNow); 8 } 9 //TimableOutput.println("one turn is over"); 10 break; 11 } 12 ArrayList<PersonRequest> tempArrayList; 13 //TimableOutput.println("here1"); 14 if (upOrDown) { 15 tempArrayList = queueOfElevator.pollUp(floorNow); 16 if (tempArrayList == null) { 17 //TimableOutput.println("no in people"); 18 if (openDoor) { 19 closeDoor(floorNow); 20 } 21 upOrDown(); 22 continue; 23 } else { 24 //TimableOutput.println(" in people"); 25 if (!openDoor) { 26 openDoor(floorNow); 27 } 28 for (int i = 0; i < tempArrayList.size(); i++) { 29 peopleInElevator.add(tempArrayList.get(i)); 30 peopleIn(tempArrayList.get(i)); 31 } 32 closeDoor(floorNow); 33 } 34 //TimableOutput.println("fuck"); 35 } else { 36 tempArrayList = queueOfElevator.pollDown(floorNow); 37 if (tempArrayList == null) { 38 //TimableOutput.println("no in people"); 39 if (openDoor) { 40 closeDoor(floorNow); 41 } 42 upOrDown(); 43 continue; 44 } else { 45 if (!openDoor) { 46 openDoor(floorNow); 47 } 48 for (int i = 0; i < tempArrayList.size(); i++) { 49 peopleInElevator.add(tempArrayList.get(i)); 50 peopleIn(tempArrayList.get(i)); 51 } 52 closeDoor(floorNow); 53 } 54 } 55 upOrDown(); 56 } 57 }
时序图:
时序图画的比较乱,大体与第二次作业类似,几个线程之间的唤醒与结束,但是多了一个调度线程调度时阻塞,被电梯线程唤醒继续工作这一流程。
4.3 同步控制实现
第二次的作业控制如下:
本次的同步控制完全由synchronized与wait(),notifyAll()实现,即在调度器调用poll时如果队列为空则会wait(),而输入线程add时则会notifyAll,同时为了避免调度器在wait时,输入线程结束而导致其永久的wait,在队列中有一个调度器是否能结束信号,输入线程在结束之前会先将其置true同时notifyAll,而poll则会在被唤醒时判断本信号为true返回null从而让调度器线程结束。此操作在电梯线程上同理。
而本次作业在这基础上考虑到现在不止是输入线程能写入队列,电梯也可以,故而调度器结束信号还包括了电梯是否都不含有需要拆分回扔的指令。
而在保证了调度器线程的存活之后,电梯线程的结束判断则可以和上次相同了。
再来说说如何让一个调度器面对三个电梯都有可能因为向电梯hashMap
插入指令,而因满员wait的情况下如何调度,我的处理是让电梯线程遇到满员并且wait()时,不适用while{wait()},而是if{wait()},这样任何一个电梯减员后将调度线程唤醒之后,电梯再判断一次当前塞请求的电梯是否满员,如果满员则将该指令不做任何处理原样返回,重新插入到Input队列中去。这样保证了电梯能分别被三个电梯阻塞,而且能够被正常唤醒运行,并且避免出现轮询的情形。
4.4 度量分析
类表:
Project Name | Package Name | Type Name | NOF | NOPF | NOM | NOPM | LOC | WMC | NC | DIT | LCOM | FANIN | FANOUT |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
hmwk_7 | elevator | Elevator | 15 | 0 | 16 | 2 | 330 | 70 | 0 | 0 | 0 | 1 | 3 |
hmwk_7 | elevator | HashMapOfElevator | 8 | 0 | 20 | 19 | 162 | 32 | 0 | 0 | 0.1 | 3 | 0 |
hmwk_7 | elevator | Input | 3 | 0 | 4 | 3 | 37 | 7 | 0 | 0 | 0 | 2 | 1 |
hmwk_7 | elevator | MainClass | 0 | 0 | 1 | 1 | 29 | 1 | 0 | 0 | -1 | 0 | 5 |
hmwk_7 | elevator | QueueFor | 6 | 0 | 9 | 8 | 75 | 17 | 1 | 0 | 0.333333 | 0 | 0 |
hmwk_7 | elevator | QueueOfInput | 0 | 0 | 1 | 0 | 5 | 1 | 0 | 1 | -1 | 4 | 0 |
hmwk_7 | elevator | Scheduler | 13 | 0 | 14 | 3 | 246 | 62 | 0 | 0 | 0 | 2 | 3 |
其中NOF (Number Of Fields)代表域个数,NOM (Number Of Methods)代表方法个数,NOPM (Number of Public Methods)代表公共方法个数,LOC (Lines of Code)代表类总代码规模(行数)。
本次作业的Elevator类内容并没有增加太多,增加了一些对于满员和拆分回扔的内容,而调度器内容则激增,也说明了本次作业主要的问题在调度器上,同时也说明了架构的成功,让电梯与调度器功能分离的比较合理。
方法表:
Project Name | Package Name | Type Name | MethodName | LOC | CC | PC |
---|---|---|---|---|---|---|
hmwk_7 | elevator | Elevator | Elevator | 17 | 3 | 9 |
hmwk_7 | elevator | Elevator | Elevator | 49 | 9 | 0 |
hmwk_7 | elevator | Elevator | Elevator | 54 | 13 | 0 |
hmwk_7 | elevator | Elevator | Elevator | 8 | 2 | 0 |
hmwk_7 | elevator | Elevator | Elevator | 23 | 6 | 0 |
hmwk_7 | elevator | Elevator | Elevator | 28 | 6 | 0 |
hmwk_7 | elevator | Elevator | Elevator | 16 | 2 | 0 |
hmwk_7 | elevator | Elevator | Elevator | 16 | 2 | 0 |
hmwk_7 | elevator | Elevator | Elevator | 12 | 5 | 1 |
hmwk_7 | elevator | Elevator | Elevator | 9 | 1 | 1 |
hmwk_7 | elevator | Elevator | Elevator | 9 | 1 | 1 |
hmwk_7 | elevator | Elevator | Elevator | 5 | 1 | 1 |
hmwk_7 | elevator | Elevator | Elevator | 14 | 3 | 1 |
hmwk_7 | elevator | Elevator | Elevator | 8 | 3 | 1 |
hmwk_7 | elevator | Elevator | Elevator | 39 | 11 | 1 |
hmwk_7 | elevator | Elevator | Elevator | 6 | 2 | 0 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 5 | 1 | 1 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 29 | 4 | 1 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 12 | 2 | 1 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 29 | 4 | 1 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 12 | 2 | 1 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 0 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 0 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 1 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 1 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 16 | 4 | 0 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 4 | 1 | 0 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 0 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 0 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 0 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 0 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 4 | 1 | 0 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 0 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 1 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 8 | 2 | 0 |
hmwk_7 | elevator | HashMapOfElevator | HashMapOfElevator | 3 | 1 | 0 |
hmwk_7 | elevator | Input | Input | 4 | 1 | 2 |
hmwk_7 | elevator | Input | Input | 3 | 1 | 0 |
hmwk_7 | elevator | Input | Input | 19 | 3 | 0 |
hmwk_7 | elevator | Input | Input | 6 | 2 | 0 |
hmwk_7 | elevator | MainClass | MainClass | 27 | 1 | 1 |
hmwk_7 | elevator | QueueFor | QueueFor | 3 | 1 | 0 |
hmwk_7 | elevator | QueueFor | QueueFor | 7 | 1 | 1 |
hmwk_7 | elevator | QueueFor | QueueFor | 20 | 3 | 0 |
hmwk_7 | elevator | QueueFor | QueueFor | 3 | 1 | 0 |
hmwk_7 | elevator | QueueFor | QueueFor | 3 | 1 | 1 |
hmwk_7 | elevator | QueueFor | QueueFor | 3 | 1 | 1 |
hmwk_7 | elevator | QueueFor | QueueFor | 4 | 1 | 0 |
hmwk_7 | elevator | QueueFor | QueueFor | 12 | 4 | 1 |
hmwk_7 | elevator | QueueFor | QueueFor | 12 | 4 | 1 |
hmwk_7 | elevator | QueueOfInput | QueueOfInput | 3 | 1 | 0 |
hmwk_7 | elevator | Scheduler | Scheduler | 8 | 1 | 6 |
hmwk_7 | elevator | Scheduler | Scheduler | 13 | 3 | 0 |
hmwk_7 | elevator | Scheduler | Scheduler | 8 | 2 | 1 |
hmwk_7 | elevator | Scheduler | Scheduler | 6 | 2 | 1 |
hmwk_7 | elevator | Scheduler | Scheduler | 17 | 4 | 1 |
hmwk_7 | elevator | Scheduler | Scheduler | 17 | 4 | 1 |
hmwk_7 | elevator | Scheduler | Scheduler | 17 | 4 | 1 |
hmwk_7 | elevator | Scheduler | Scheduler | 15 | 4 | 1 |
hmwk_7 | elevator | Scheduler | Scheduler | 45 | 14 | 1 |
hmwk_7 | elevator | Scheduler | Scheduler | 25 | 7 | 1 |
hmwk_7 | elevator | Scheduler | Scheduler | 25 | 7 | 1 |
hmwk_7 | elevator | Scheduler | Scheduler | 25 | 7 | 1 |
hmwk_7 | elevator | Scheduler | Scheduler | 4 | 1 | 0 |
hmwk_7 | elevator | Scheduler | Scheduler | 6 | 2 | 0 |
分析程序有一些bug无法看到方法名,但是每个类的长度都不长。除了Elevator中的两个有关运行逻辑的方法,run()和operate(),以及Scheduler中的任务分配函数。
度量分析表:
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
elevator.Elevator.Elevator(String,Scheduler,QueueOfInput,HashMapOfElevator,ArrayList<Integer>,ArrayList<Integer>,int,int,String) | 1 | 3 | 3 |
elevator.Elevator.badRequestOut(PersonRequest) | 9 | 6 | 11 |
elevator.Elevator.closeDoor(int) | 1 | 2 | 2 |
elevator.Elevator.down() | 1 | 3 | 3 |
elevator.Elevator.find() | 6 | 5 | 6 |
elevator.Elevator.getTo(int) | 1 | 5 | 5 |
elevator.Elevator.isBadRequest(PersonRequest) | 3 | 2 | 3 |
elevator.Elevator.openDoor(int) | 1 | 2 | 2 |
elevator.Elevator.operate() | 6 | 13 | 13 |
elevator.Elevator.peopleIn(PersonRequest) | 1 | 1 | 1 |
elevator.Elevator.peopleOut(PersonRequest) | 1 | 3 | 3 |
elevator.Elevator.run() | 7 | 10 | 11 |
elevator.Elevator.someoneDestinates() | 3 | 9 | 10 |
elevator.Elevator.start() | 1 | 2 | 2 |
elevator.Elevator.up() | 1 | 3 | 3 |
elevator.Elevator.upOrDown() | 1 | 2 | 2 |
elevator.HashMapOfElevator.HashMapOfElevator(int) | 1 | 1 | 1 |
elevator.HashMapOfElevator.addDown(PersonRequest) | 2 | 4 | 5 |
elevator.HashMapOfElevator.addNumOfBadRequest() | 1 | 1 | 1 |
elevator.HashMapOfElevator.addNumOfPeople() | 1 | 1 | 1 |
elevator.HashMapOfElevator.addUp(PersonRequest) | 2 | 4 | 5 |
elevator.HashMapOfElevator.awake() | 1 | 1 | 1 |
elevator.HashMapOfElevator.containDown(int) | 1 | 1 | 1 |
elevator.HashMapOfElevator.containUp(int) | 1 | 1 | 1 |
elevator.HashMapOfElevator.getDieAndAway() | 1 | 1 | 1 |
elevator.HashMapOfElevator.getHashSizeDown() | 1 | 1 | 1 |
elevator.HashMapOfElevator.getHashSizeUp() | 1 | 1 | 1 |
elevator.HashMapOfElevator.getNumOfBadRequest() | 1 | 1 | 1 |
elevator.HashMapOfElevator.getNumOfPeople() | 1 | 1 | 1 |
elevator.HashMapOfElevator.isFull() | 2 | 1 | 2 |
elevator.HashMapOfElevator.pollDown(int) | 2 | 2 | 2 |
elevator.HashMapOfElevator.pollUp(int) | 2 | 2 | 2 |
elevator.HashMapOfElevator.setMaxOfNum(int) | 1 | 1 | 1 |
elevator.HashMapOfElevator.sleepWaiting() | 3 | 4 | 7 |
elevator.HashMapOfElevator.subNumOfBadRequest() | 1 | 1 | 1 |
elevator.HashMapOfElevator.subNumOfPeople() | 1 | 1 | 1 |
elevator.Input.Input(String,QueueOfInput) | 1 | 1 | 1 |
elevator.Input.isDown() | 1 | 1 | 1 |
elevator.Input.run() | 3 | 4 | 4 |
elevator.Input.start() | 1 | 2 | 2 |
elevator.MainClass.main(String[]) | 1 | 1 | 1 |
elevator.QueueFor.QueueFor() | 1 | 1 | 1 |
elevator.QueueFor.add(PersonRequest) | 1 | 1 | 1 |
elevator.QueueFor.awakeAndGoDie() | 1 | 1 | 1 |
elevator.QueueFor.elevatorWantAlive(String) | 1 | 3 | 4 |
elevator.QueueFor.elevatorWantToDie(String) | 1 | 3 | 4 |
elevator.QueueFor.getQueueSize() | 1 | 1 | 1 |
elevator.QueueFor.poll() | 3 | 3 | 7 |
elevator.QueueFor.printAdd(PersonRequest) | 1 | 1 | 1 |
elevator.QueueFor.printPoll(PersonRequest) | 1 | 1 | 1 |
elevator.QueueOfInput.QueueOfInput() | 1 | 1 | 1 |
elevator.Scheduler.Scheduler(String,QueueOfInput,HashMapOfElevator,HashMapOfElevator,HashMapOfElevator,Input) | 1 | 1 | 1 |
elevator.Scheduler.canTakeA(PersonRequest) | 7 | 4 | 7 |
elevator.Scheduler.canTakeB(PersonRequest) | 7 | 4 | 7 |
elevator.Scheduler.canTakeC(PersonRequest) | 7 | 4 | 7 |
elevator.Scheduler.distribute(PersonRequest) | 1 | 2 | 2 |
elevator.Scheduler.distributeOther(PersonRequest) | 1 | 32 | 32 |
elevator.Scheduler.distributeSingle(PersonRequest) | 1 | 4 | 4 |
elevator.Scheduler.inSingle(PersonRequest) | 2 | 3 | 4 |
elevator.Scheduler.isOK() | 1 | 9 | 9 |
elevator.Scheduler.operateA(PersonRequest) | 1 | 4 | 4 |
elevator.Scheduler.operateB(PersonRequest) | 1 | 4 | 4 |
elevator.Scheduler.operateC(PersonRequest) | 1 | 4 | 4 |
elevator.Scheduler.run() | 3 | 6 | 7 |
elevator.Scheduler.start() | 1 | 2 | 2 |
Class | OCavg | WMC | |
elevator.Elevator | 4.38 | 70 | |
elevator.HashMapOfElevator | 1.6 | 32 | |
elevator.Input | 1.75 | 7 | |
elevator.MainClass | 1 | 1 | |
elevator.QueueFor | 1.89 | 17 | |
elevator.QueueOfInput | 1 | 1 | |
elevator.Scheduler | 4.43 | 62 | |
Package | v(G)avg | v(G)tot | |
elevator | 3.72 | 242 | |
Module | v(G)avg | v(G)tot | |
hmwk_7 | 3.72 | 242 | |
Project | v(G)avg | v(G)tot | |
project | 3.72 | 242 |
本次基本与上次类似,但是有一个及其巨大的函数,是调度器的分配函数,由于该函数内含16个if-else连续判断,并且每个条件至少2个因子的连接,从而导致其复杂度及其巨大,而这可以通过判断中间过程是否执行,来拆分降低其复杂度。或者通过改变架构,让三个电梯争抢同一容器内的指令来取消巨大的if-else链。
4.5 程序评价
本次作业基本套用了上次作业的框架,只是在判断是否满员,在调度器的决策上改动较多,本次最大的问题体现在架构的不够优秀上,该架构确实在电梯与调度器的功能划分上表现得比较优秀,但是在细节决策上思考不够仔细,而导致了后期工作经常卡壳。
比如对可拆分指令的拆分回写,对于其拆分点的决策思考不够仔细,从而导致了需要打很多补丁来保证其正确性。
同时本次的鲁棒性还是很差,没有在电梯接受指令的WF上进行考虑,选择了完全相信调度器的正确性。
本次作业的性能不是非常好,思考了一下可能是由于调度器的硬性if-else链的僵硬性导致的,可以选择形成一个电梯共同指令池,电梯去争抢指令可能会提高效率,但是由于这样会违背我的初衷”让电梯和调度器做自己的事“,或者让调度器能够知道电梯的当前状态从而精准分配。
同时本次程序的电梯线程的鲁棒性极低,完全由程序结构,以及调度器的正确运行所保证。即没有在电梯线程中写遇到WF的操作。
对程序进行SOLID 原则分析
SRP:每个类的职责都很明确,但是有一个方法位置不对,同时有三个方法过于复杂。
OCP:基本满足。
LSP:没有子类。
ISP:未使用接口
DIP:符合,高层次模块依赖于依赖于低层次的抽象。
五. 分析BUG
由于基本通过了强测,除了第6次作业有一个点由于硬编码出现bug。
而谈谈在编程时遇到的bug,绝大多数甚至90%出现在线程在不该结束的时候结束上,而对共享变量的维护由于使用了sychronized
所以没有出现过bug。
而出现线程结束的原因主要有两个:
1.没弄清楚其结束的条件到底是什么,或者说是该线程有必要活下去的条件。
2.以为弄清楚了结束条件,但是没有考虑到线程调度过程中会出现的状况,而没有将条件补充到最完备的情况。
同时还出现了初始化线程时随意初始化参数,而在之后的线程实例化时再去set,而太低估线程运行的速度导致bug。
此附上一个判断程序正确的脚本:
import re
import time
import sys
if __name__ == '__main__':
with open('*.txt', 'r') as f:
total_time = 0
for line in f.readlines():
matchObj = re.match(r'\[(.*)\](.*)', line, re.M|re.I)
cur_time = float(matchObj.group(1))
time.sleep(cur_time-total_time)
print(matchObj.group(2))
sys.stdout.flush()
total_time = cur_time
此脚本按照时间戳输出指令,并且通过管道传输给程序,来模拟评测机上的输出。
六.心得体会