OO第二次博客作业
第一次作业:套用生产者消费者模式。电梯作为一个单独线程,是消费者。输入线程是生产者。全局只有一个任务队列的共享对象。
第二次作业:采用生产者消费者模式。耦合比较乱的地方:电梯作为一个单独线程,同时肩负起调度器和电梯的角色。
第三次作业:采用类似于流水线的模式(其实还是生产者消费者模式,不过就是一环套一环)∶输入线程读取输入,放入一级任务队列。计算器线程从前面任务队列取出任务,根据电梯停靠楼层等信息,对任务进行拆分,放入二级任务队列。分发器线程从前面任务队列取出任务,决定该任务是否应该分发出去,以及应该分发给哪一个电梯。每个电梯线程对应于一个任务队列,其之后的结构类似于第二次作业的结构。整体上还是静态调度,没有根据当前电梯的状态动态分配任务。本来以为这种多重依赖的结构会比较慢,结果运行效果也还可以。
总得来说,这三次作业以《面向对象∶从重构到重写》为基本指导思想,充分发扬了艰苦奋斗,能吃苦能熬夜的精神,将生产者消费者模式一用到底,完成了三次代码的从0到1。架构太辣鸡,重构?重构是解决不了问题的,勇敢的人直接重写。
度量分析
第一次作业,第二次作业
采取同样的设计模式:生产者消费者模式。
UML图基本相同:
类复杂度分析:
方法复杂度分析,只列出了部分可以反应问题的:
可以看出电梯内耦合调度器,甚至共享对象还承担了部分调度的逻辑,这其实是很不合理的。导致共享对象有几个方法,电梯的几个方法复杂度非常高。在第三次作业中,对电梯代码进行了部分重构,但效果还是不尽人意。讲电梯运行逻辑和调度逻辑解耦才是王道。但是在这三次作业中并没有实现。
第三次作业
UML图:流水线式架构,为了使主要结构看起来更清晰,省略了Request类(对PersonRequest的封装类,主要用于把一个请求分解成两个请求)。
有Request类时:
类复杂度
主要方法复杂度
只列出反应一定问题的:
可以看出电梯类的复杂度异常的高,也就是电梯和其自身调度器没有实现解耦,就算多次重构,其复杂度也高居不下。而与电梯相关联的共享对象也承担了一部分的调度逻辑。复杂度也较高。应该讲该共享对象和调度逻辑封装成一个新类。
自己bug的分析
靠着手写评测机强测互测没有被炸点
第三次作业自测出的bugs:
-
CPU超时。
如果不是开评测机时CPU占用太高,根本不会注意到这个bug。问题在于分发器线程是个轮询线程,而没有用到wait,notify。
-
线程同步。
在某人还没有出电梯时,就通知接这个人的电梯该接了。问题在于”电梯关门这个动作“和”通知下一个电梯这个人可以接这个动作“的先后次序。
找别人bug的策略
先每人挂4个评测机跑2000个测试点再说
-
完全随机。力求模拟测评机
(并没有做到)。完全随机的测试数据的生成,一般会使得每条请求比较分散,如果有死锁的话可能(”可能“两个字就意味着我要开始瞎说了)会比较容易测出来。 -
一个小时间段集中生成测试点。比如:0.1时刻生成40条请求。比较考验线程安全。每一个线程瞬时处理的任务都变多,如果同步没做好,可能会在这部分炸点。(实际效果上,集中式生成要比完全随机生成更容易发现bug)
-
看了强测的测试用例,发现还有一种情况没注意到,多个时间点集中生成测试样例。比如0.0时刻输入10条请求,30.0时刻再输入15条请求。看得出这应该是精心处理过的样例,专门用来炸一些代码的中止电梯的逻辑。
-
还有针对代码覆盖率等度量来构造数据集。哈~ 这个没用过,还在学习中。。
心得体会
-
这次苟进A组的经历让我有了”原来这就是强者的世界“这样的感叹。个人觉得互测的意义其实还是提供了这样一个源码分析,技术交流的机会,看大佬们的代码真的能学到很多东西。刀人其实都是次要的。所以在保证正确性的基础上,努力提高强测成绩,争取互测时可以看到大佬源码才是进步的最快方法。
(是不是可以考虑互测结束后,每个房间开放一段时间的聊天室功能 -
为了能顺利苟进A组看到大佬们的代码,评测机这个东西还是要经常写,不然心理不踏实。不能因为一些小错让强测炸点。不值得。
-
关于代码可扩展性的一些分析和感悟:
对比自己代码和大佬代码,发现自己代码可扩展性差的几点原因
-
对电梯的建模有数字硬编码的问题
事实上初始楼层,上行时间,下行时间,载客量,停靠时间,开关门时间这些静态信息都应该作为电梯的基本参数,便于统一建模。
-
电梯运行逻辑和调度策略耦合在一起
当前楼层,当前电梯状态,当前任务队列等动态信息应该来让调度器来管理。这样便实现了电梯和电梯调度器的解耦,从而也允许了电梯可以采用不同的调度策略。类似于strategy的设计模式∶调度器定义统一接口,不同的调度器具体实现。
-
最开始写代码时没有考虑可扩展性是根本原因
还在正确性边缘挣扎的菜鸡,把代码写完后才会想到可扩展性这一出。刚写完代码时的心理活动:“我怎么这么牛B,这架构完美无敌”;下一次作业看自己上次作业代码时的心理活动:“这写的倒是个**,这也太辣鸡了,自己都不想看”;互测看到大佬的代码时的心理活动:“这才是面向对象编程啊!这可扩展性也太高了吧”。
-
看了很多design pattern,但不会灵活运用
很多pattern就是看一眼觉得大概可以用,但具体实现又觉得情况不怎么一样。互测看过大佬代码才知道,“奥~ 这个pattern要这么写啊~“。所以三次作业最后都只用到了消费者生产者这一种模式。同组大佬将单例模式(输入输出全局调度器的管理),strategy模式(不同电梯具体采用不同的调度算法),Composite模式(实现对任务类的包装,将一条任务递归得拆分成多个任务链)用的出神入化。。(可能还有没看出来的设计模式)
-
个人代码还是有一定可复用性(强行扯皮)