OO Unit2 总结 博客作业
18373806 冯天昱
一、设计策略
本单元的三次作业依然是逐步迭代开发的过程,第一次作业是单部电梯,到第二次作业的容量限制的多部电梯,再到第三次电梯的多类型电梯和电梯动态加入,三次迭代的过程与第一单元相比顺畅了许多,这得益于第一次作业中采用的设计的一些特点。
个人的设计采用了典型的生产者-消费者模式,整个设计中共存在两类线程——主线程和电梯线程,和一个共享对象——调度器。其中主线程同时担任了输入线程的工作,即不断地从输入中获取请求并放入调度器,而电梯线程在需要时访问调度器,将符合条件的请求从调度器中取出并放入电梯自己的待完成请求队列中,同时将调度器中自身的状态记录更新以为调度算法提供依据。
这一设计策略决定了电梯对于整个调度系统具有绝对的主动性,调度器仅作为一个共享对象,只有电梯主动访问时才通过调度器的算法获得自己需要的请求。因此在第二次作业中,即使加入了多个电梯,做出的改变也仅仅是增加几个电梯线程,更改调度算法以适应电梯的容量限制和多部电梯之间的协作而已,调度器并不管理自己与几部电梯进行通信。
至于第三次作业,增加了电梯的可停靠楼层限制,因此在电梯类中增加了对应的信息;同时该限制还导致了一些请求不得不通过多部电梯协作完成,个人的设计中采用了静态分割,在请求加入调度器前直接将请求分割为若干子请求以交由多部电梯完成,同时对请求添加依赖关系以解决时间先后问题。在这些完成之后,利用前两次作业的现有结构,针对需求对其他实现细节稍作修改就可以完成整个作业的要求了。
二、可扩展性分析
在第二单元作业的过程中,由于对多线程协作的相关知识掌握不是很扎实,我一直饱受线程安全问题困扰,将过多的精力放置在线程安全问题的解决上,导致整个设计的架构比较糟糕:没有模块化设计导致类的规模十分庞大,每一个问题域中的类——输入、调度器、电梯等,都与一个程序中的类一一对应,这也导致程序没有可扩展性可言,因为任何的需求改动都要直接导致对一个或多个现有的类动刀——调度算法是硬编码在调度器的方法中的,电梯的运行策略是写死在代码中的,调度器中电梯的状态记录并没有封装,而是分别通过多个关联数组(Map)直接存储并直接访问使用的,等等。
下面从SOLID原则的五个方面分别审视自己最终的(第三次作业)设计:
-
S——Single Responsibility Principle(单一职责原则):
个人的作业近乎完美地违背了这一原则,每一个类的职责都十分庞大,电梯类、调度器类的代码行数都达到数百行,所有的逻辑全部写在了这些类的方法中。
-
O——Open Closed Principle(开闭原则):
刚刚提到,在我的设计中要想对程序进行改动,必须对现有的代码动刀,意味着违反了“闭”原则。与此同时,代码中仅针对电梯、调度器等大类进行了抽象,因此若要说没有一点扩展性也不对,至少电梯、调度器还是可以通过继承来进行扩展的,但这样的实现难度很大,与重写没什么区别了。因此,“开”原则也没有很好的遵守。
-
L——Liskov Substitution Principle(里氏替换原则):
L原则的内容是:”所有引用基类的地方必须能透明地使用其子类的对象“,而我的设计中压根就没有用到自定义的继承关系,因此没有违背该原则。
-
I——Interface Segregation Principle(接口分离原则):
在这方面我的设计做的还算不错,因为整体上来看类之间的交互并不多,虽然类比较大,但接口都比较小,耦合度不是很高。
-
D——Dependency Inversion Principle(依赖倒置原则):
与针对里氏替换原则的分析相同,由于程序中并没有使用继承,也没有使用模块化,依赖倒置原则中的”高级模块不依赖低级模块“压根无从谈起,而”抽象不依赖于细节“也随着程序中根本就没有接口、抽象类的存在变得没有意义。
总的来说,由于我的程序太过面向过程化,SOLID原则中的许多原则对程序的分析压根无从下手,更不要谈是否能够很好的遵守了。
三、程序结构度量和分析
第一次作业:
- UML类图:
- UML协作图:
- Complexity Metrics度量:
Class | OCavg | WMC |
---|---|---|
oo.elevator.MainClass | 3 | 3 |
oo.elevator.elevator.Elevator | 2.63 | 50 |
oo.elevator.elevator.ElevatorFactory | 14 | 14 |
oo.elevator.elevator.ElevatorScheduler | 3 | 21 |
oo.elevator.elevator.ElevatorSpecException | 1 | 2 |
oo.elevator.elevator.status.Direction | n/a | 0 |
oo.elevator.elevator.status.Person | 1 | 4 |
oo.elevator.utils.ElevatorOutput | 1.29 | 9 |
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
oo.elevator.MainClass.main(String[]) | 3 | 3 | 4 |
oo.elevator.elevator.Elevator.arrive() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.capacity() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.checkDirection() | 10 | 10 | 16 |
oo.elevator.elevator.Elevator.close() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.getInPassengersAndConvert() | 1 | 3 | 3 |
oo.elevator.elevator.Elevator.getOutPassengersAndRemove() | 1 | 3 | 3 |
oo.elevator.elevator.Elevator.getRequest() | 4 | 5 | 5 |
oo.elevator.elevator.Elevator.goDown() | 1 | 1 | 3 |
oo.elevator.elevator.Elevator.goUp() | 1 | 1 | 3 |
oo.elevator.elevator.Elevator.idle() | 1 | 2 | 2 |
oo.elevator.elevator.Elevator.move() | 1 | 2 | 2 |
oo.elevator.elevator.Elevator.movingFor(long) | 1 | 2 | 2 |
oo.elevator.elevator.Elevator.open() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.openingFor(long) | 1 | 2 | 2 |
oo.elevator.elevator.Elevator.run() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.stoppable() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.transfer() | 2 | 4 | 5 |
oo.elevator.elevator.Elevator.work() | 1 | 4 | 4 |
oo.elevator.elevator.ElevatorFactory.makeElevator(String,ElevatorScheduler) | 3 | 5 | 17 |
oo.elevator.elevator.ElevatorScheduler.dispatchable(int,Direction,PersonRequest,boolean) | 4 | 10 | 10 |
oo.elevator.elevator.ElevatorScheduler.findNearestRequestAndRemove(int) | 2 | 3 | 4 |
oo.elevator.elevator.ElevatorScheduler.getDirection(PersonRequest) | 2 | 1 | 2 |
oo.elevator.elevator.ElevatorScheduler.putRequest(PersonRequest) | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorScheduler.takeRequests(int,Direction,boolean,boolean,boolean) | 1 | 8 | 8 |
oo.elevator.elevator.ElevatorScheduler.terminate() | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorScheduler.terminated() | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorSpecException.ElevatorSpecException() | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorSpecException.ElevatorSpecException(String) | 1 | 1 | 1 |
oo.elevator.elevator.status.Person.Person(int,int) | 1 | 1 | 1 |
oo.elevator.elevator.status.Person.getId() | 1 | 1 | 1 |
oo.elevator.elevator.status.Person.getToFloor() | 1 | 1 | 1 |
oo.elevator.elevator.status.Person.toString() | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printArrive(int) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printClose(int) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printIn(int,int) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printIns(ArrayList |
1 | 2 | 2 |
oo.elevator.utils.ElevatorOutput.printOpen(int) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printOut(int,int) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printOuts(ArrayList |
1 | 2 | 2 |
第二次作业:
- UML类图:
- UML协作图:
- Complexity Metrics度量:
Class | OCavg | WMC |
---|---|---|
oo.elevator.MainClass | 3 | 3 |
oo.elevator.elevator.Elevator | 2.32 | 51 |
oo.elevator.elevator.ElevatorFactory | 15 | 15 |
oo.elevator.elevator.ElevatorScheduler | 3.7 | 37 |
oo.elevator.elevator.ElevatorSpecException | 1 | 2 |
oo.elevator.elevator.status.Direction | n/a | 0 |
oo.elevator.elevator.status.Person | 1 | 4 |
oo.elevator.elevator.status.Request | 1 | 6 |
oo.elevator.utils.ElevatorOutput | 1.56 | 14 |
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
oo.elevator.MainClass.main(String[]) | 3 | 3 | 4 |
oo.elevator.elevator.Elevator.arrive() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.capacity() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.checkDirection() | 10 | 10 | 16 |
oo.elevator.elevator.Elevator.close() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.currentFloor() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.getInPassengersAndConvert() | 1 | 3 | 3 |
oo.elevator.elevator.Elevator.getOutPassengersAndRemove() | 1 | 3 | 3 |
oo.elevator.elevator.Elevator.getRequest() | 4 | 4 | 5 |
oo.elevator.elevator.Elevator.goDown() | 1 | 1 | 2 |
oo.elevator.elevator.Elevator.goUp() | 1 | 1 | 2 |
oo.elevator.elevator.Elevator.idle() | 1 | 2 | 2 |
oo.elevator.elevator.Elevator.move() | 1 | 2 | 2 |
oo.elevator.elevator.Elevator.movingFor(long) | 1 | 2 | 2 |
oo.elevator.elevator.Elevator.open() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.openingFor(long) | 1 | 2 | 2 |
oo.elevator.elevator.Elevator.remainingCapacity() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.requestCount() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.run() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.stoppable() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.transfer() | 2 | 4 | 5 |
oo.elevator.elevator.Elevator.work() | 1 | 4 | 4 |
oo.elevator.elevator.ElevatorFactory.makeElevator(String,ElevatorScheduler) | 3 | 5 | 18 |
oo.elevator.elevator.ElevatorScheduler.ElevatorScheduler(int) | 1 | 3 | 3 |
oo.elevator.elevator.ElevatorScheduler.calcNextDirection(boolean,Direction,LinkedList ,int) | 7 | 5 | 8 |
oo.elevator.elevator.ElevatorScheduler.dispatchable(int,Direction,Request,boolean) | 4 | 10 | 10 |
oo.elevator.elevator.ElevatorScheduler.findNearestRequestAndRemove(int,Elevator) | 8 | 12 | 15 |
oo.elevator.elevator.ElevatorScheduler.getDirection(Request) | 2 | 1 | 2 |
oo.elevator.elevator.ElevatorScheduler.putRequest(Request) | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorScheduler.start() | 1 | 2 | 2 |
oo.elevator.elevator.ElevatorScheduler.takeRequests(int,Direction,boolean,boolean,boolean,Elevator) | 1 | 10 | 10 |
oo.elevator.elevator.ElevatorScheduler.terminate() | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorScheduler.terminated() | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorSpecException.ElevatorSpecException() | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorSpecException.ElevatorSpecException(String) | 1 | 1 | 1 |
oo.elevator.elevator.status.Person.Person(int,int) | 1 | 1 | 1 |
oo.elevator.elevator.status.Person.getId() | 1 | 1 | 1 |
oo.elevator.elevator.status.Person.getToFloor() | 1 | 1 | 1 |
oo.elevator.elevator.status.Person.toString() | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.Request(PersonRequest) | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.getFromFloor() | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.getPersonId() | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.getToFloor() | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.isNull() | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.toString() | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printArrive(int,String) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printClose(int,String) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printIn(int,int,String) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printIns(ArrayList |
1 | 2 | 2 |
oo.elevator.utils.ElevatorOutput.printOpen(int,String) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printOut(int,int,String) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printOuts(ArrayList |
1 | 2 | 2 |
oo.elevator.utils.ElevatorOutput.progToRealFloor(int) | 2 | 1 | 2 |
oo.elevator.utils.ElevatorOutput.realToProgFloor(int) | 3 | 1 | 3 |
第三次作业:
- UML类图:
- UML协作图:
电梯获取请求时并不关心自己是哪一种类的电梯,具体请求分配的规则是由调度器决定的。
- Complexity Metrics度量:
Class | OCavg | WMC |
---|---|---|
oo.elevator.MainClass | 5 | 5 |
oo.elevator.elevator.Elevator | 2.34 | 68 |
oo.elevator.elevator.ElevatorFactory | 4.6 | 23 |
oo.elevator.elevator.ElevatorScheduler | 2.95 | 56 |
oo.elevator.elevator.ElevatorSpecException | 1 | 2 |
oo.elevator.elevator.status.Direction | n/a | 0 |
oo.elevator.elevator.status.Request | 1.1 | 11 |
oo.elevator.test.DividerTester | 4 | 4 |
oo.elevator.utils.ElevatorOutput | 1.56 | 14 |
oo.elevator.utils.RequestDivider | 1.63 | 31 |
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
oo.elevator.MainClass.main(String[]) | 3 | 5 | 6 |
oo.elevator.elevator.Elevator.arrive() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.capacity() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.checkDirection() | 14 | 9 | 14 |
oo.elevator.elevator.Elevator.close() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.currentFloor() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.debug_PrintNewRequests(ArrayList ) | 2 | 1 | 2 |
oo.elevator.elevator.Elevator.debug_PrintRemainingRequests() | 1 | 3 | 3 |
oo.elevator.elevator.Elevator.debug_PrintTerminationDetected() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.debug_PrintWait() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.equals(Object) | 3 | 2 | 4 |
oo.elevator.elevator.Elevator.getCarMovingTime() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.getInPassengers() | 1 | 3 | 3 |
oo.elevator.elevator.Elevator.getOutPassengersAndRemove() | 1 | 3 | 3 |
oo.elevator.elevator.Elevator.getRequest() | 5 | 5 | 6 |
oo.elevator.elevator.Elevator.goDown() | 1 | 1 | 2 |
oo.elevator.elevator.Elevator.goUp() | 1 | 1 | 2 |
oo.elevator.elevator.Elevator.hashCode() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.idle() | 1 | 2 | 2 |
oo.elevator.elevator.Elevator.move() | 1 | 2 | 2 |
oo.elevator.elevator.Elevator.movingFor(long) | 1 | 2 | 2 |
oo.elevator.elevator.Elevator.open() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.openingFor(long) | 1 | 2 | 2 |
oo.elevator.elevator.Elevator.remainingCapacity() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.requestCount() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.run() | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.stoppable(int) | 1 | 1 | 1 |
oo.elevator.elevator.Elevator.transfer() | 2 | 6 | 7 |
oo.elevator.elevator.Elevator.work() | 1 | 4 | 6 |
oo.elevator.elevator.ElevatorFactory.makeATypeElevator(String,ElevatorScheduler) | 1 | 2 | 2 |
oo.elevator.elevator.ElevatorFactory.makeBTypeElevator(String,ElevatorScheduler) | 1 | 2 | 2 |
oo.elevator.elevator.ElevatorFactory.makeCTypeElevator(String,ElevatorScheduler) | 1 | 2 | 2 |
oo.elevator.elevator.ElevatorFactory.makeElevator(String,String,ElevatorScheduler) | 5 | 5 | 5 |
oo.elevator.elevator.ElevatorFactory.makeElevatorFromSpecs(String,ElevatorScheduler) | 3 | 5 | 18 |
oo.elevator.elevator.ElevatorScheduler.attachElevator(Elevator) | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorScheduler.calcNextDirection(boolean,Direction,LinkedList ,int) | 7 | 5 | 8 |
oo.elevator.elevator.ElevatorScheduler.debug_PrintIdleStatus(boolean) | 1 | 2 | 2 |
oo.elevator.elevator.ElevatorScheduler.debug_PrintPuttedRequest(Request) | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorScheduler.debug_PrintUnhandledRequests() | 1 | 2 | 2 |
oo.elevator.elevator.ElevatorScheduler.dispatchable(int,Direction,Request,boolean) | 2 | 2 | 2 |
oo.elevator.elevator.ElevatorScheduler.dispatchableForwarding(int,Direction,Request) | 2 | 6 | 6 |
oo.elevator.elevator.ElevatorScheduler.dispatchableTurning(int,Direction,Request) | 2 | 6 | 6 |
oo.elevator.elevator.ElevatorScheduler.findNearestRequestAndRemove(int,Elevator) | 11 | 15 | 20 |
oo.elevator.elevator.ElevatorScheduler.getDirection(Request) | 2 | 1 | 2 |
oo.elevator.elevator.ElevatorScheduler.getToReadyDirection(int,Elevator) | 5 | 5 | 6 |
oo.elevator.elevator.ElevatorScheduler.putRequest(Request) | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorScheduler.reportBusy(Elevator) | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorScheduler.reportIdle(Elevator) | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorScheduler.start() | 1 | 2 | 2 |
oo.elevator.elevator.ElevatorScheduler.stoppable(Request,Elevator) | 1 | 2 | 2 |
oo.elevator.elevator.ElevatorScheduler.takeRequests(int,Direction,boolean,boolean,boolean,Elevator) | 7 | 11 | 14 |
oo.elevator.elevator.ElevatorScheduler.terminate() | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorScheduler.terminated() | 1 | 2 | 2 |
oo.elevator.elevator.ElevatorSpecException.ElevatorSpecException() | 1 | 1 | 1 |
oo.elevator.elevator.ElevatorSpecException.ElevatorSpecException(String) | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.Request(PersonRequest) | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.Request(int,int,int) | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.Request(int,int,int,Request) | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.getFromFloor() | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.getPersonId() | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.getPrev() | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.getToFloor() | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.isReady() | 2 | 1 | 2 |
oo.elevator.elevator.status.Request.satisfy() | 1 | 1 | 1 |
oo.elevator.elevator.status.Request.toString() | 1 | 1 | 1 |
oo.elevator.test.DividerTester.main(String[]) | 1 | 4 | 4 |
oo.elevator.utils.ElevatorOutput.printArrive(int,String) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printClose(int,String) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printIn(int,int,String) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printIns(ArrayList ,int,String) | 1 | 2 | 2 |
oo.elevator.utils.ElevatorOutput.printOpen(int,String) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printOut(int,int,String) | 1 | 1 | 1 |
oo.elevator.utils.ElevatorOutput.printOuts(ArrayList ,int,String) | 1 | 2 | 2 |
oo.elevator.utils.ElevatorOutput.progToRealFloor(int) | 2 | 1 | 2 |
oo.elevator.utils.ElevatorOutput.realToProgFloor(int) | 3 | 1 | 3 |
oo.elevator.utils.RequestDivider.divideFromThird(Request) | 1 | 2 | 2 |
oo.elevator.utils.RequestDivider.divideHighDown(Request) | 1 | 2 | 2 |
oo.elevator.utils.RequestDivider.divideHighUp(Request) | 1 | 1 | 1 |
oo.elevator.utils.RequestDivider.divideLowDown(Request) | 1 | 1 | 1 |
oo.elevator.utils.RequestDivider.divideLowUp(Request) | 1 | 2 | 2 |
oo.elevator.utils.RequestDivider.divideRequest(Request) | 7 | 7 | 7 |
oo.elevator.utils.RequestDivider.divideToThird(Request) | 1 | 2 | 2 |
oo.elevator.utils.RequestDivider.inRangeInclusive(int,int,int) | 1 | 1 | 2 |
oo.elevator.utils.RequestDivider.isHighFloor(int) | 1 | 1 | 1 |
oo.elevator.utils.RequestDivider.isHigherFloor(int) | 1 | 1 | 1 |
oo.elevator.utils.RequestDivider.isLowFloor(int) | 1 | 1 | 1 |
oo.elevator.utils.RequestDivider.isLowerFloor(int) | 1 | 1 | 1 |
oo.elevator.utils.RequestDivider.isUnreachableWithThird(int) | 3 | 1 | 3 |
oo.elevator.utils.RequestDivider.shouldDivideFromThird(Request) | 1 | 2 | 2 |
oo.elevator.utils.RequestDivider.shouldDivideHighDown(Request) | 1 | 2 | 2 |
oo.elevator.utils.RequestDivider.shouldDivideHighUp(Request) | 1 | 2 | 2 |
oo.elevator.utils.RequestDivider.shouldDivideLowDown(Request) | 1 | 2 | 2 |
oo.elevator.utils.RequestDivider.shouldDivideLowUp(Request) | 1 | 2 | 2 |
oo.elevator.utils.RequestDivider.shouldDivideToThird(Request) | 1 | 2 | 2 |
四、自身BUG分析
我在本单元的第二次作业中其实出现了一个较为严重的BUG——电梯会在达到最低楼层之后试图再下降一层,然而这个BUG需要非常严苛的测试用例才能以较低的概率复现,因此在强测和互测中并没有被发现。
这个BUG出现的原因是多方面的,下面逐一分析。
我的电梯使用了LOOK算法,在电梯发现当前方向没有继续前进的必要时就会掉头,而在掉头之前电梯会不甘心地再获取一次请求,如果此次请求获取到了,就不掉头。调度器判断电梯某一次获取请求时是否为“即将掉头”的情况是使用一个布尔型变量显式判断的,但由于调度器的代码逻辑出现问题,会导致电梯在“所有请求执行完毕”时一定会为其分配一个最近的请求,不管电梯是在常规时刻还是在“即将掉头”这样的特殊时刻。这直接导致了电梯会在送完当前自己待处理请求队列里的所有请求后发现需要掉头,而此时由于电梯是空闲的,调度器会无条件地为其分配一个请求,这个请求可能是前方的,也可能是需要掉头的,电梯拿到请求并没有判断这个请求是前方还是后方,只以“是否没有获得到请求”作为是否掉头的依据,因此它不会掉头而是继续按当前方向运行。
单独这一个缺陷并不会直接导致电梯运行错误,因为电梯会在到达边界并试图移动时强行修正方向,因此即使电梯没有掉头,并且要继续移动,这一修正也能保证电梯的运行时正确的。催生这一BUG的另一原因是:最小楼层数解析错误。为了程序运行方便,我将-3~-1层映射到程序中的-2~0层,但在创建电梯时传入电梯工厂使用的最小楼层还是-3,此时的-3已经对应着问题域(实际情况)中的-4层了,因此对于上述情况修正并不会起作用,因为它在电梯运行到-3层(程序中的-2层)时认为电梯还没有到达最低楼层,电梯就会继续下降一层到-4。
幸运的是,电梯在出现这个BUG之前一定会在当前楼层完成最后一个请求的下电梯(出人)操作,否则不会在此时同时满足”即将掉头“和”所有请求执行完毕“。出人意味着一定要开门,而在我的程序中,电梯会在即将关门前获得一次请求,而”即将掉头“的判断是紧接着关门的,这导致了新请求很可能在关门前被电梯接受,继而当紧接着的”即将掉头“获得请求时电梯已经有新请求了,”所有请求执行完毕“不成立,BUG就不会出现。
五、测试策略
在互测中,我采用了完全的黑盒测试,并在评测程序报告BUG之后检查代码,试图定位BUG。然而,在三次作业中我都没有成功分析出我找到的BUG出现的位置,比较失望。 这些BUG都是难以发现和复现的,想必原作者在对代码进行改动时也会相当困难。
为了增加BUG发生的几率,我在生成数据时构造了许多边界情况,比如大量从底层到顶层的请求,大量上行和下行交叉的请求,(在第三次作业中)大量以3楼为起点或终点的请求等等。
与第一单元相比,本单元的测试更加依赖于黑盒测试,因为本次单元之间协作的正确性对程序整体正确性的影响比第一单元单线程程序中要大得多,通过白盒测试来定位BUG对我个人来说是很困难的。
六、心得体会
线程安全是贯穿整个单元的核心问题,也是最让我头秃的问题。在最一开始着手编写代码的时候并没有了解过多的相关内容,导致我花费了两天的时间寻找电梯不能正常结束的原因,并且借此机会学习了volatile关键字、atomic类等内容,对synchronized关键字的理解也随着作业的完成、实验和课程内容的学习逐步加深,同时近期操作系统也开始了进程同步的相关内容,两门课程的内容互相印证、互相完善,让我对并发程序设计有个更深的体会。
多线程程序设计中又出现了许多典型的设计模式和设计架构,这些模式和架构都是传统单线程程序所没有的,其中不仅体现了传统设计模式中的扩展性、模块化要求,还很好地适应了不同并发场景的实际需求,比如生产者-消费者模式、Worker模式、线程池、Future模式等等,这些设计模式又可以和更加普适的设计模式,如状态模式、策略模式等等相结合,在本单元的作业中也能够得到很好的应用。遗憾的是,个人考虑到时限和编码的复杂性,并没有明显地使用上述的某一种设计模式。
在这一单元我将过多的精力放在保证程序能够正确的执行和正常退出上,结果导致程序的设计并没有体现出太多的可扩展性,许多在第一单元结束后阅读和了解的设计模式也没有运用进来,是不足之处。但我想程序的正确性始终是最重要的,对于需求方来说错误的程序毫无意义,因此比重失衡是个人能力有限导致的,但也是必须的,毕竟一个正确的设计得不是很好的程序多数情况下是要好过一个设计优美但频频出错的程序的。不过既然是在学习过程中,在不能够做到两者兼备的情况下如何取舍,我个人也没有绝对的定论(但正确性是评判作业是否合法的硬性指标,我想在实际生产和应用中也是如此)。