OO第二单元总结——多线程电梯
第五次作业分析
1.设计策略
调度器采用单例模式,内部设请求队列,对请求队列的一切操作(查、增、删)都在调度器内完成,且都要求串行,从而确保线程安全。接收器和电梯是两个线程:接收器接受请求调用调度器来存入请求队列,接受器关闭时通知调度器;电梯调用调度器来获得请求,电梯从调度获得空请求且查询到接受器关闭时停止运行。
2.度量分析
(1)复杂度矩阵
方法复杂度:
类复杂度:
由于这一次作业是简单的单电梯傻瓜调度,所以方法和类复杂度都比较低。
(2)类图
(3)协作图
3.bug分析
没有bug。
第六次作业分析
1.设计策略
与上次作业相比,本次作业仅仅增加了捎带,故整体策略不变,仅改变电梯调度的算法为扫描算法且增加捎带功能。
由于在调度器,我们只关注乘客的出发楼层和目标方向,而在电梯内,只关注乘客的目标楼层。所以可以将调度器中的请求队列分割为上行请求队列和下行请求队列以及乘客目标楼层图,在电梯内新增楼层到达图:
上行请求队列:每一层有哪些乘客要上行;
下行请求队列:每一层有哪些乘客要下行;
乘客目标楼层图:每一位乘客要在哪一层出电梯;
楼层到达图:每一层有哪些乘客要出电梯。
实现捎带:
电梯到达每一层时向调度器发送捎带请求,调度器将对应方向请求队列中该层的乘客的目标楼层图返回给电梯,电梯捎带这些乘客,将他们加入对应楼层到达图对应楼层的乘客列表。
实现扫描算法:
电梯属性中新增方向和目标楼层。
电梯中没有乘客(当前楼层为目标楼层),选择主请求(新的目标楼层)时:
①选择方向与电梯当前方向一致,且出发楼层在电梯当前楼层的当前方向上的最近请求,若存在这种请求:
若该请求在当前楼层,则将该乘客目标楼层设为电梯目标楼层;
否则将该请求出发楼层作为电梯目标楼层。
②若不存在①中那种请求,则选择方向与电梯当前方向相反,但出发楼层在电梯当前楼层的当前方向上的最远请求,若存在这种请求,将电梯方向取反:
若该请求在当前楼层,则将该乘客目标楼层设为电梯目标楼层;
否则将该请求出发楼层作为电梯目标楼层。
③若不存在②中那种请求,则选择方向与电梯当前方向相反,且出发楼层在电梯当前楼层的当前方向反方向上的最近请求,若存在这种请求,将电梯方向取反:
若该请求在当前楼层,则将该乘客目标楼层设为电梯目标楼层;
否则将该请求出发楼层作为电梯目标楼层。
④若不存在③中那种请求,则选择方向与电梯当前方向相同,但出发楼层在电梯当前楼层的当前方向反方向上的最远请求,若存在这种请求:
将该请求出发楼层作为电梯目标楼层。
⑤若不存在④中那种请求,说明当前没有请求,若接收器关闭则停止运行电梯,否则等待请求到来。
每一次捎带后:
若电梯上行,存在乘客的目标楼层中最高的在电梯目标楼层更高层,则将该楼层设为电梯目标楼层;
若电梯下行,存在乘客的目标楼层中最低的在电梯目标楼层更低层,则将该楼层设为电梯目标楼层。
2.度量分析
(1)复杂度矩阵
方法复杂度:
类复杂度:
扫描调度算法的实现稍微有些复杂,乘客进入电梯和更新目标楼层的方法要遍历对应的捎带图,导致电梯循环复杂度稍高。
(2)类图
(3)协作图
4.bug分析
没有bug。
第七次作业分析
1.设计策略
本次作业为多电梯调度,每个电梯有对应的可达楼层。
我采用的是静态分配请求的策略。新增一个分配器,每个请求刚刚收到就规划好其需要搭乘的第一部电梯及对应的出电梯的楼层,将其分配给对应电梯的调度器,每个电梯由自己的独立的调度器根据自己内部的请求进行独立调度,每个请求出电梯后若未到达目标楼层再新生成请求提交到分配器。
这种策略简单但在一些情况下效率可能不高。
2.度量分析
(1)复杂度矩阵
方法复杂度:
类复杂度:
除了捎带和扫描算法导致的复杂度问题,这次的分配器为每一个可能的x层到y层的请求选择第一次搭乘的电梯和出电梯楼层的方法也较为复杂,另外在主方法中手动创建每个电梯的可达楼层列表使用不少循环导致主方法循环复杂度较高。
(2)类图
(3)协作图
4.bug分析
由于开始写的时间太晚,导致压着时间点完成,最后忙中出错,没有初始化时间戳,这一次作业无效了。
SOLID原则
1.SRP(单一责任原则)
个人感觉三次作业每个类的责任都比较单一,最后一次可能应该写一个类来创建电梯,不应该直接放在主方法中。
2.OCP(开放封闭原则)
第五次作业的调度器接收请求的方法在第六次进行了重写,因为改变了请求队列的存储方式。
3.LSP(里氏替换原则)
没有父子类,不讨论。
4.ISP(接口分离原则)
三次作业的接口设置的都较为合理,没有这方面的问题。
5.DIP(依赖倒置原则)
三次作业的类间依赖都是自然合理的,没有这方面的问题。
总结与心得
多线程编程的debug是一个头疼的大问题,所以一定要从一开始就小心谨慎,尽量避免程序中出现bug。
另外,最重要的:
永远不要拖延!