OO第二单元博客作业
面向对象第二单元博客作业
0. 前言
本单元三次作业均成功提交,比上一单元有了一定进步。但由于没有进行充分的测试等原因,三次作业均出现了严重的失误,第一二次作业均没有通过强测进入互测阶段,第三次作业尽管进入了互测,但也出现了死锁问题。到现在进行总结时,我对OO课程的心态已经发生了一些变化……
1. 三次作业的设计策略
第一次作业:输入与电梯调度器之间采用生产者消费者模式,电梯调度器在得到请求之后将请求派送给电梯的等待队列中,电梯的调度策略采用ALS策略(预期,实际上由于设计不当退化为FCFS,后修改bug时改为look策略)。
第二次作业:输入与电梯调度器之间仍然采用生产者消费者模式,电梯调度器在得到请求以后将请求放置在一个公共等待队列中,所有电梯均可访问该队列,以电梯类为同步读写锁。吸取第一次作业的教训,对性能进行了极大的优化,通过乘客提供的计算电梯与乘客之间的距离的方法,实现动态分配乘客(预期,实际上有可能出现多部电梯相应一个请求的方法)。电梯内部使用look策略。
第三次作业:输入与电梯调度器之间仍然采用生产者消费者模式,使用两层调度器,主调度器与电梯调度器之间采用Worke_Thread模式,电梯调度器与电梯之间使用第一次作业的模型。吸取第二次作业的教训,在进行性能优化的同时保证功能的正确性,同类型的不同电梯之间的分配策略采用随机分配的方式。对于需要换乘的乘客,设置标志和中转楼层(1楼或15楼),在前一部电梯将其送至中转楼层以后,将请求发送给主调度器,由主调度将其分配给下一部电梯(这个过程中发生了死锁……)。
2. 第三次作业架构设计的可扩展性
- 单一职责原则:修改了前两次作业中电梯的功能,将电梯的调度完全交给电梯调度器。
- 开闭原则:本次的不足是将电梯定义为了一个类,应当将电梯设计为一个父类或者接口,然后由不同种类的电梯来继承或实现它,否则可扩展性不足。
- 依赖倒置原则:在前一点的基础上,电梯调度器应当调度电梯的父类或接口中的方法来提高可扩展性。
3. 程序结构分析
第一次作业:
method | ev(G) | iv(G) | v(G) |
---|---|---|---|
Elevator.add(ArrayList>) | 1.0 | 3.0 | 3.0 |
Elevator.Arrive() | 1.0 | 1.0 | 1.0 |
Elevator.Close() | 1.0 | 2.0 | 2.0 |
Elevator.Downstair() | 1.0 | 3.0 | 4.0 |
Elevator.Elevator() | 1.0 | 2.0 | 2.0 |
Elevator.getIn() | 1.0 | 4.0 | 5.0 |
Elevator.getOut() | 1.0 | 2.0 | 2.0 |
Elevator.moveTo(int) | 1.0 | 8.0 | 8.0 |
Elevator.Open() | 1.0 | 2.0 | 2.0 |
Elevator.run() | 4.0 | 4.0 | 6.0 |
Elevator.setMoveToFloor() | 5.0 | 10.0 | 19.0 |
Elevator.Stop() | 1.0 | 1.0 | 1.0 |
Elevator.Upstair() | 1.0 | 3.0 | 4.0 |
Main.main(String[]) | 1.0 | 1.0 | 1.0 |
Request.Request(RequestQueue) | 1.0 | 1.0 | 1.0 |
Request.run() | 3.0 | 3.0 | 4.0 |
RequestQueue.get() | 3.0 | 5.0 | 6.0 |
RequestQueue.getStop() | 1.0 | 1.0 | 1.0 |
RequestQueue.isEmpty() | 1.0 | 1.0 | 1.0 |
RequestQueue.put(PersonRequest) | 1.0 | 2.0 | 3.0 |
RequestQueue.RequestQueue() | 1.0 | 2.0 | 2.0 |
Scheduler.run() | 3.0 | 4.0 | 4.0 |
Scheduler.Scheduler(RequestQueue,Elevator) | 1.0 | 1.0 | 1.0 |
Total | 36.0 | 66.0 | 83.0 |
Average | 1.56 | 2.87 | 3.61 |
可以看出,第一次作业将电梯的运行方法完全交给电梯来完成,等待队列与乘客队列也交给电梯来维护,这样的设计扩展性较差,不符合单一职责原则。
第二次作业:
Elevator.AddPerson(Person) | 1.0 | 1.0 | 1.0 |
---|---|---|---|
Elevator.Arrive() | 1.0 | 1.0 | 1.0 |
Elevator.Check() | 1.0 | 3.0 | 3.0 |
Elevator.Close() | 1.0 | 2.0 | 2.0 |
Elevator.DownStair() | 1.0 | 3.0 | 5.0 |
Elevator.Elevator(int) | 1.0 | 2.0 | 2.0 |
Elevator.getCurrentFloor() | 1.0 | 1.0 | 1.0 |
Elevator.getDirection() | 1.0 | 1.0 | 1.0 |
Elevator.getIn() | 3.0 | 3.0 | 4.0 |
Elevator.getOut() | 1.0 | 2.0 | 2.0 |
Elevator.Move() | 1.0 | 3.0 | 3.0 |
Elevator.Open() | 1.0 | 2.0 | 2.0 |
Elevator.run() | 4.0 | 4.0 | 6.0 |
Elevator.searchDown() | 1.0 | 3.0 | 4.0 |
Elevator.searchUp() | 1.0 | 3.0 | 4.0 |
Elevator.setMoveToFloor() | 1.0 | 5.0 | 5.0 |
Elevator.Stop() | 1.0 | 1.0 | 1.0 |
Elevator.UpStair() | 1.0 | 3.0 | 5.0 |
Main.main(String[]) | 1.0 | 1.0 | 1.0 |
Person.getDirection() | 1.0 | 1.0 | 1.0 |
Person.getFromFloor() | 1.0 | 1.0 | 1.0 |
Person.getPersonId() | 1.0 | 1.0 | 1.0 |
Person.getToFloor() | 1.0 | 1.0 | 1.0 |
Person.Person(PersonRequest) | 1.0 | 1.0 | 2.0 |
Request.Request(RequestQueue) | 1.0 | 1.0 | 1.0 |
Request.run() | 3.0 | 4.0 | 4.0 |
RequestQueue.get() | 3.0 | 3.0 | 4.0 |
RequestQueue.getStop() | 1.0 | 1.0 | 1.0 |
RequestQueue.isEmpty() | 1.0 | 1.0 | 1.0 |
RequestQueue.put(Person) | 1.0 | 2.0 | 2.0 |
RequestQueue.RequestQueue() | 1.0 | 1.0 | 1.0 |
Scheduler.AllocPerson(Person) | 4.0 | 7.0 | 7.0 |
Scheduler.elevatorsStop() | 1.0 | 2.0 | 2.0 |
Scheduler.run() | 3.0 | 3.0 | 4.0 |
Scheduler.Scheduler(RequestQueue,int) | 1.0 | 1.0 | 1.0 |
Scheduler.SchedulerInit() | 1.0 | 2.0 | 2.0 |
Total | 50.0 | 77.0 | 89.0 |
Average | 1.39 | 2.14 | 2.47 |
第二次作业大部分沿用了第一次作业的结构,此外新设计了一个Person类来管理PersonRequest的方向,是否被电梯响应,请求所在的楼层与电梯所在楼层的距离等信息。
第三次作业:
Elevator.Arrive() | 1.0 | 1.0 | 1.0 |
---|---|---|---|
Elevator.Close() | 1.0 | 2.0 | 2.0 |
Elevator.DownStair() | 1.0 | 3.0 | 5.0 |
Elevator.Elevator(String,String,ElevatorScheduler) | 1.0 | 1.0 | 1.0 |
Elevator.ElevatorInit() | 2.0 | 2.0 | 5.0 |
Elevator.getCurrentFloor() | 1.0 | 1.0 | 1.0 |
Elevator.getDirection() | 1.0 | 1.0 | 1.0 |
Elevator.getIn(int) | 1.0 | 1.0 | 1.0 |
Elevator.getMaxFloor() | 1.0 | 1.0 | 1.0 |
Elevator.getMinFloor() | 1.0 | 1.0 | 1.0 |
Elevator.getOut(int) | 1.0 | 1.0 | 1.0 |
Elevator.isEmpty() | 1.0 | 1.0 | 1.0 |
Elevator.isFull() | 1.0 | 1.0 | 1.0 |
Elevator.move() | 1.0 | 3.0 | 3.0 |
Elevator.Open() | 1.0 | 2.0 | 2.0 |
Elevator.run() | 3.0 | 2.0 | 3.0 |
Elevator.setDirection(int) | 1.0 | 1.0 | 1.0 |
Elevator.UpStair() | 1.0 | 3.0 | 5.0 |
ElevatorRequest.ElevatorRequest(RequestQueue) | 1.0 | 1.0 | 1.0 |
ElevatorRequest.run() | 3.0 | 3.0 | 4.0 |
ElevatorScheduler.AddPerson(Person,int) | 1.0 | 1.0 | 1.0 |
ElevatorScheduler.Check() | 1.0 | 12.0 | 12.0 |
ElevatorScheduler.ElevatorScheduler(MainScheduler,String,String) | 1.0 | 2.0 | 2.0 |
ElevatorScheduler.ElevatorStart() | 1.0 | 1.0 | 1.0 |
ElevatorScheduler.run() | 1.0 | 3.0 | 3.0 |
ElevatorScheduler.searchDown() | 1.0 | 3.0 | 4.0 |
ElevatorScheduler.searchUp() | 1.0 | 3.0 | 4.0 |
ElevatorScheduler.setMoveToFloor() | 7.0 | 14.0 | 16.0 |
ElevatorScheduler.Stop() | 1.0 | 1.0 | 1.0 |
Main.main(String[]) | 1.0 | 1.0 | 1.0 |
MainScheduler.AddElevator(String,String) | 1.0 | 1.0 | 1.0 |
MainScheduler.AddPerson(Person) | 1.0 | 1.0 | 1.0 |
MainScheduler.AllocPerson() | 1.0 | 4.0 | 5.0 |
MainScheduler.MainScheduler(RequestQueue) | 1.0 | 1.0 | 1.0 |
MainScheduler.run() | 3.0 | 11.0 | 11.0 |
MainScheduler.SchedulerInit() | 1.0 | 2.0 | 2.0 |
Person.dockAble(int,int) | 3.0 | 1.0 | 3.0 |
Person.getDirection() | 1.0 | 1.0 | 1.0 |
Person.getFromTransfer() | 1.0 | 1.0 | 1.0 |
Person.getGetFrom() | 1.0 | 1.0 | 1.0 |
Person.getTransferTo() | 1.0 | 1.0 | 1.0 |
Person.getTransferToFloor() | 1.0 | 1.0 | 1.0 |
Person.isHasToTransfer() | 1.0 | 1.0 | 1.0 |
Person.Person(int,int,int) | 1.0 | 1.0 | 2.0 |
Person.PersonInit() | 1.0 | 8.0 | 12.0 |
Person.Transferred() | 1.0 | 1.0 | 1.0 |
RequestQueue.get() | 3.0 | 3.0 | 4.0 |
RequestQueue.isEmpty() | 1.0 | 1.0 | 1.0 |
RequestQueue.isStop() | 1.0 | 1.0 | 1.0 |
RequestQueue.put(Request) | 1.0 | 2.0 | 3.0 |
RequestQueue.RequestQueue() | 1.0 | 1.0 | 1.0 |
Total | 68.0 | 118.0 | 141.0 |
Average | 1.33 | 2.31 | 2.76 |
第三次作业与第二次作业相比做出了一定改动,将电梯的调度从电梯交给电梯调度器。
第三次作业UML协作图
4. 自己程序中的bug
第一次作业:电梯中队列错误采用了一维的形式,也就说,当电梯判断是否进行捎带时,只会去判断当前队列队首的请求,这导致设计的ALS策略退化为FCFS策略,性能大大下降。
改进措施:将电梯队列改为二维,同时改用look策略以提高性能。
第二次作业:由于过度强调性能,忽视了程序的正确性,导致出现很多神秘bug。
改进措施:将动态分配人员改为随机分配
第三次作业:本次作业的设计采用了两层调度器,原本的程序中出现需要中转的乘客时,电梯的调度器直接调用主调度器的分配方法将该乘客转运给下一个电梯。但是由于主调度器的分配方法、电梯调度器接受乘客的方法都为synchronized类型,因此当某电梯调度器向主调度器返回乘客,同时主调度器向电梯调度器分配乘客时,会产生死锁。
改进措施:在主调度器中再定义一个等待队列,用来处理电梯调度器返回的这部分乘客。
5. 心得体会
- 我对这三次作业最大的体会是程序性能与功能正确性之间的矛盾:在第一次作业中追求正确性而忽视了性能,在第二次作业中又片面追求了性能而出现了莫名奇妙的bug。但是很多时候这种矛盾是因为我的设计缺陷或者测试不到位造成的,如果对程序进行充分的迭代,我相信自己可以写出兼顾性能与功能的程序。
- OO课程已经进行了一半,我从这短暂的两个月中学到一件事——越是努力完成作业,越能看见补给站向我招手,除非超越自己...