OO第二单元小结
第一次作业
设计策略
这次作业要求是用多线程实现一台电梯,我采用的生产者-消费者模式,
调度线程和电梯线程构成两对生产者和消费者,Inplat负责管理输入的数据和相关方法,wplat负责管理电梯相关信息和相关信息处理方法,调度线程负责放置任务。在线程功能上,我采用类似硬件的方式,电梯每次从调度队列拿走所有任务,然后执行任务,这里的任务并不是reques,而是up,down,open,close,in。电梯楼层每改变一次,就会重新读取调度队列,电梯每次in,out,就会分别更新wplat中的数据。在调度策略上,由于我的任务方式,所以每次调度器要做的就是确定电梯是up还是down,是否open、in、out。我的方案是若电梯内有目的地不是本层的乘客,则根据乘客需求确定方向。反之从电梯外寻找出发地不在本层的乘客确定前进方向。在确定前进方向后再根据方向判断本层是否要开门,若电梯外有人前进方向和当前电梯预期运行方向一致,则开门放人。我认为这样设计调度比较灵活,而且各个类的结构分摊比较均匀。在线程同步上,wplat和inplat都是带锁方法,同时调度线程的work方法要获得wpalt的锁,电梯的work方法同样也要获得wplat的锁,这样的好处在于,调度线程每次工作时,电梯必然停止运行状态,逻辑非常清晰,反之亦然。这样虽然看起来电梯和调度器无法"同时"运行,但是实际上即使不带锁电梯和调度器也无法"同时"运行,所谓的同时不过是轮流占用CPU,考虑到CPU运算时间相对于电梯运行时间相比少之又少,这样其实好处远多于弊处。
代码分析
UML类图
复杂度分析
可以看到大部分方法复杂度都较低,唯一标红的方法为调度和核心方法,即为确定电梯前进方向的方法。
BUG&测试
在这次作业中我自我测试主要是通过IDEA设置断点然后自己来调度的方式,这种方式有点是可以全部由自己掌控,缺点是非常占用时间。通过这种方式我找到了自己的一个死锁bug.这次作业并没有搭建自动评测及,所以在寻找别人bug上主要靠自己手造数据,并没有hack成功.
第二次作业
设计策略
这次的任务要支持多电梯。在功能设计,上本次的设计中为了平衡各个类的功能,我将putorder的方法放到了电梯和调度线程的贡献类中,而调度线程的功能则变为了从request中选择合适的request放入wplat类中,order策略仍然和上次一样。在request的放入上,我的分配策略是若电梯下一次预测运行方向正朝着当前乘客的出发楼层前进,且电梯的wplat中 request数量不多,则放入电梯中。现在想来每次选择电梯前可以先把电梯排个序。在线程同步上,第一次的同步方法有些弊端,调度器容易被某个正在运行的电梯卡住,会一直获取锁。现在想来电梯运行其实不必要一直占用锁,只要把队列中的命令去取出即可。
代码分析
UML类图
复杂度分析
这次复杂度增加一个主要原因在于没有0层,所以每个循环都有 if(i==0) continue;
BUG&测试
这次测试,主要采用输出信息的方式来调试,有点在于非常快速便捷。本次利用python搭建了一个简单的复现输入程序,在互测阶段没有发现别人BUG。
第三次作业
设计策略
这次任务支持动态加入电梯且电梯有停靠楼层限制。功能设计上,这次电梯的创建有main函数转移到了Elvfactory上,然后调度线程可以通过Elvfactory增加电梯。针对换乘问题采用任务分割的方式,参考观察者模式来保证任务的执行先后顺序,Pathset的静态方法提供了任务风格。在调度上,其他方面于任务二基本一致,不同的就是Wplat产生order时直会让已经ready的person进入电梯。任务分割采用的静态测量,本地先打好表。从表现看,完全静态调度非常糟糕,同时由于有未ready的方法,电梯可能会朝着一个没有准备好的对象跑去而忽略已经准备好的方法。现在看来,最少换乘+静态表的方式可能比较合适。线程同步上,和作业二观点一致。
代码分析
UML类图
个人认为每个类的方法都比较均衡。
复杂度
可以看到,于调度优化相关的方法复杂度较高。
BUG&测试
测试仍然是输出调试,非常好用!互测方面,本来想hack别人,找到自己的bug......再次证明了本地测试非常重要!!
可拓展性
SRP(单一职责): 调度器功和Wplat功能过多,其他部分功能较为单一。
OCP(开闭原则): ELVfactory满足构造不同电梯的需求,Pathset可以对路径规划,基本满足扩展需求。
LSP(可替换原则): 本次作业没有采用继承。
ISP(接口分离): 本次作业未使用接口,使用时可以考虑按照层次创造接口。
DIP(依赖倒置原则): 本次作业并没有接口,主要还是依赖具体的类。但是可以考虑向上造型创造接口,从而依赖接口。
心得体会
1.本周的作业迭代非常顺利,基本满足了OCP原则,每次对原有架构的修改非常少,一方面是整体迭代变化比较少,另外一个方面也是可能在前期的设计比较注意各个类功能的均衡
2.对于复杂问题求解,面面俱到是不可能的。要善于抓主要矛盾,舍弃细枝末节的优化而选择一个更为清晰的逻辑不失为上乘之选。比如这第一次作业为了让逻辑更为清晰选择电梯工作时调度器不工作的策略等。
3.对于并发编程和线程间同步理解加深,知道从调度顺序的角度来思考程序可能出现bug和程序运行情况等。
4.测试仍然不够充分,每次任然以完成任务为目标,没有测试的激情和动力,也没有好好利用中测来优化自己性能,这是下次需要注意的点。