OO第二次博客作业

设计策略

第一次作业:套用生产者消费者模式。电梯作为一个单独线程,是消费者。输入线程是生产者。全局只有一个任务队列的共享对象。

第二次作业:采用生产者消费者模式。耦合比较乱的地方:电梯作为一个单独线程,同时肩负起调度器和电梯的角色。

第三次作业:采用类似于流水线的模式(其实还是生产者消费者模式,不过就是一环套一环)∶输入线程读取输入,放入一级任务队列。计算器线程从前面任务队列取出任务,根据电梯停靠楼层等信息,对任务进行拆分,放入二级任务队列。分发器线程从前面任务队列取出任务,决定该任务是否应该分发出去,以及应该分发给哪一个电梯。每个电梯线程对应于一个任务队列,其之后的结构类似于第二次作业的结构。整体上还是静态调度,没有根据当前电梯的状态动态分配任务。本来以为这种多重依赖的结构会比较慢,结果运行效果也还可以。

 

总得来说,这三次作业以《面向对象∶从重构到重写》为基本指导思想,充分发扬了艰苦奋斗,能吃苦能熬夜的精神,将生产者消费者模式一用到底,完成了三次代码的从0到1。架构太辣鸡,重构?重构是解决不了问题的,勇敢的人直接重写。

 

度量分析

第一次作业,第二次作业

采取同样的设计模式:生产者消费者模式。

UML图基本相同:

类复杂度分析:

方法复杂度分析,只列出了部分可以反应问题的:

可以看出电梯内耦合调度器,甚至共享对象还承担了部分调度的逻辑,这其实是很不合理的。导致共享对象有几个方法,电梯的几个方法复杂度非常高。在第三次作业中,对电梯代码进行了部分重构,但效果还是不尽人意。讲电梯运行逻辑和调度逻辑解耦才是王道。但是在这三次作业中并没有实现。

第三次作业

UML图:流水线式架构,为了使主要结构看起来更清晰,省略了Request类(对PersonRequest的封装类,主要用于把一个请求分解成两个请求)。

 有Request类时:

 

类复杂度

 

主要方法复杂度

只列出反应一定问题的:

 

 

可以看出电梯类的复杂度异常的高,也就是电梯和其自身调度器没有实现解耦,就算多次重构,其复杂度也高居不下。而与电梯相关联的共享对象也承担了一部分的调度逻辑。复杂度也较高。应该讲该共享对象和调度逻辑封装成一个新类。

自己bug的分析

靠着手写评测机强测互测没有被炸点

第三次作业自测出的bugs:

  1. CPU超时。

    如果不是开评测机时CPU占用太高,根本不会注意到这个bug。问题在于分发器线程是个轮询线程,而没有用到wait,notify。

  2. 线程同步。

    在某人还没有出电梯时,就通知接这个人的电梯该接了。问题在于”电梯关门这个动作“和”通知下一个电梯这个人可以接这个动作“的先后次序。

找别人bug的策略

先每人挂4个评测机跑2000个测试点再说

说一下测试点生成的几个策略:

  1. 完全随机。力求模拟测评机(并没有做到) 。完全随机的测试数据的生成,一般会使得每条请求比较分散,如果有死锁的话可能(”可能“两个字就意味着我要开始瞎说了)会比较容易测出来。

  2. 一个小时间段集中生成测试点。比如:0.1时刻生成40条请求。比较考验线程安全。每一个线程瞬时处理的任务都变多,如果同步没做好,可能会在这部分炸点。(实际效果上,集中式生成要比完全随机生成更容易发现bug)

  3. 看了强测的测试用例,发现还有一种情况没注意到,多个时间点集中生成测试样例。比如0.0时刻输入10条请求,30.0时刻再输入15条请求。看得出这应该是精心处理过的样例,专门用来炸一些代码的中止电梯的逻辑。

  4. 还有针对代码覆盖率等度量来构造数据集。哈~ 这个没用过,还在学习中。。

心得体会

  1. 这次苟进A组的经历让我有了”原来这就是强者的世界“这样的感叹。个人觉得互测的意义其实还是提供了这样一个源码分析,技术交流的机会,看大佬们的代码真的能学到很多东西。刀人其实都是次要的。所以在保证正确性的基础上,努力提高强测成绩,争取互测时可以看到大佬源码才是进步的最快方法。(是不是可以考虑互测结束后,每个房间开放一段时间的聊天室功能

  2. 为了能顺利苟进A组看到大佬们的代码,评测机这个东西还是要经常写,不然心理不踏实。不能因为一些小错让强测炸点。不值得。

  3. 关于代码可扩展性的一些分析和感悟:

 

对比自己代码和大佬代码,发现自己代码可扩展性差的几点原因
  1. 对电梯的建模有数字硬编码的问题

    事实上初始楼层,上行时间,下行时间,载客量,停靠时间,开关门时间这些静态信息都应该作为电梯的基本参数,便于统一建模。

  2. 电梯运行逻辑和调度策略耦合在一起

    当前楼层,当前电梯状态,当前任务队列等动态信息应该来让调度器来管理。这样便实现了电梯和电梯调度器的解耦,从而也允许了电梯可以采用不同的调度策略。类似于strategy的设计模式∶调度器定义统一接口,不同的调度器具体实现。

  3. 最开始写代码时没有考虑可扩展性是根本原因

    还在正确性边缘挣扎的菜鸡,把代码写完后才会想到可扩展性这一出。刚写完代码时的心理活动:“我怎么这么牛B,这架构完美无敌”;下一次作业看自己上次作业代码时的心理活动:“这写的倒是个**,这也太辣鸡了,自己都不想看”;互测看到大佬的代码时的心理活动:“这才是面向对象编程啊!这可扩展性也太高了吧”。

  4. 看了很多design pattern,但不会灵活运用

    很多pattern就是看一眼觉得大概可以用,但具体实现又觉得情况不怎么一样。互测看过大佬代码才知道,“奥~ 这个pattern要这么写啊~“。所以三次作业最后都只用到了消费者生产者这一种模式。同组大佬将单例模式(输入输出全局调度器的管理),strategy模式(不同电梯具体采用不同的调度算法),Composite模式(实现对任务类的包装,将一条任务递归得拆分成多个任务链)用的出神入化。。(可能还有没看出来的设计模式)

  5. 个人代码还是有一定可复用性(强行扯皮)

    可复用也不一定是在最初就猜到之后哪里都需要扩展,进而设计好接口或者将对应部分参数化。也可以是前一次设计构成下一次设计的一个子模块,而下一次的主逻辑主架构重写。前者就像是主框架非常棒,扩展时就换换零件就可以了,是零件适应框架。后者就像是零件做的还可以,扩展时尽量让新的框架能用到旧零件。是框架适应零件。前者是大佬的代码,后者是我的代码。

posted @ 2019-04-23 20:27  Cheney0114  阅读(124)  评论(0编辑  收藏  举报