【面向对象】二单元电梯作业总结

写在前面的话

  经过坎坷的三次作业,我认为电梯单元确实比表达式单元要难许多。相比于表达式单元的“不知道怎么写”,电梯难在“不知道为什么错”。由于是多线程,这单元的作业很容易出现线程调度或者线程安全问题。而这些问题,本地测试不一定能测出来,并且也无法调试debug,因而难度很大。在这三次作业中,我都采用了“楼层类设计“,代码结构相似。虽然没有优化,并且架构不是很标准,但是我认为我的程序思路清晰。下面,我将详细分析自己三次程序。另外,值得批评的是,我这几次都是使用System.exit异常退出的…我的main线程在创造晚其他线程之后就死亡了。

下面是我三次作业中都使用的楼层类和人类:

     


第一次 傻瓜电梯

  • 设计策略

    这次我的设计比较简单,没有考虑捎带。

                                 

                  控制器单元状态机设计                                                                                                                                          电梯状态机设计

  这次作业我的核心设计是两个轮询的状态机。控制器处于被动状态,时刻检测电梯的状态,根据电梯的状态安排电梯行进路线。在电梯行进过程中,只会到达特定楼层,不会中途捎带停车。由于我设计了楼层类,并且使用HashMap作为设计结构,故我能直接接走这一楼层所有成员。总的来讲,这次作业写的思路还是很清晰的,但是轮询方法使用不当,CPU时间很长。

  • 度量分析
  1. 类图

      

  我没有单独开设input类。

      2. 复杂度分析       

 

  可以看出,我的“病态函数”基本上都是集中在状态机中。在两个有状态机的Control和Elevator线程中,结构复杂度严重超标。这样不仅使代码风格变差,美观程度降低,还会会导致错误的产生。在计算方向(calculate方法)中,我使用了两个for循环的方式检查上下楼有没有人,因而导致ev指标超标。我认为可以尝试通过一个循环的方式解决,但是那样的话就会产生更多的if语句,因而我有些纠结。

3. 时序图

       

   由于轮询,elevator和control之间就像疯掉了一样,疯狂传递信息。

     4. 设计原则:

  • Single Responsibility Principle:基本符合单一职责原则。
  • Open Close Principle:如果不考虑轮询,电梯扩展性还是很强的。
  • bug分析

  这次作业中我没有发现bug。

  • 互测经验

  这次作业中我没有找出同组同学的bug。

第二次 ALS策略

  • 设计策略

程序运行过程:

  1. 启动输入、调度器、电梯线程。
  2. 读入一个PersonQequest,实例化一个”人“,加入楼层类之中。
  3. 调度器检测有没有空闲电梯,如果没有就wait,直到被休息下来的电梯唤醒。有空闲电梯后计算电梯向哪个楼层行驶。
  4. 电梯向aimfloor行驶,中途在每一个楼层检测有没有可以捎带的人。在运行过程中,始终选择电梯中人所去的最高楼层作为aimfloor。

  我用的是Look算法。我认为这个算法可以比als快。另外这次我的floor中保存了两个hashmap,分别是toUpFloor和toDownFloor。这次我需要注意的问题是Floor加人和取人时间重合;Floor取人和检测是否有人时间重合;检测是否有人和加人时间重合。由于我的共享队列只有Floor,我就直接在get和set和remove中添加了synchronize。

  • 度量分析

  1. 类图

        

2. 复杂度分析

    

  可见,复杂程度高的还是run函数和各种判断结束函数。导致复杂度超高的原因之一是因为大量的if语句。电梯类由于方法数量太多了,因而爆红。电梯类有23个方法,一半多都是get和set;另一半基本上是各种计算楼层和进出电梯。

  3. 时序图

  

  可以看出我input开启其他线程的强行补丁……elevator线程之和floor(我把它看成了托盘)互动,这样还是分工明确的。Control的话,设置电梯的aimfloor并唤醒电梯,然后自己再被FLoor唤醒。我结束线程的方式是Control判断电梯和楼层是否有人。所以,control也会和end线程保持联系。我觉得这次作业其实我写的还是很清晰的。

  4. 设计原则:

  • Single Responsibility Principle:基本符合单一职责原则。
  • Open Close Principle:如果不考虑轮询,电梯扩展性还是很强的。
  • bug分析

  这次公测中我出现了线程调度bug。在读入第一条指令之后,控制器“不知道”楼层上还有人,因而会陷入wait中不能退出。因而,我的程序对于有些点没有输出。并且,由于多线程的随机性,给评测机交多次代码后,每次错的点不一样。我的解决办法是读入一条指令之后再开启其它线程。这样“治标不治本”,但是能暂时解决办法。我还想的一种对策是没过500毫秒,重新判断一次,这样可以避免死锁。

  我还有一个bug是电梯到达了maxfloor + 1楼层。我的解决方式是如果到达最高楼层,给电梯强制换向。

  • 互测经验

  这次互测,我发现了有些同学的线程安全bug。虽然自己写的评测机无法测出来,但是我看他在input之后使用了sleep方法,证明他也遇到了我公测时候出现的bug。但是,这种方法危险极大。如果评测机压力很大,短时间的sleep是不够的!因而我找出了别人的错误。

   我很喜欢这次作业的编程过程,但是最后由于为了一点优化分,我把自己的代码结构改的很差劲。比如本来我设计的是人不会上相反方向的电梯,但是为了少关门一次,我设计成了人上电梯,很不好。总体来讲,我的程序架构还是很清晰的。

第三次 多电梯

  • 设计策略

  这次由于有换乘的人,因此我在第二次作业的基础上给人加入了Direction特征向量。调度器的作用还是像第二次作业一样的鸡肋,没有分配人的功能,只是发送消息让电梯去抢人。我还加入了ElevatorMap,这样可以快速检测十分可以换乘。所以这次的代码几乎就是上次的多电梯个性化版本。老师提到过用继承的方法继承三个电梯出来,但是我觉得我写的电梯基本上一样的,所以没有那样做。当然,那样做可以使用工厂模式,还是很不错的。

          

  除了这些,我还将一些信号量(start,end)设计成了“空的类”。

         

  • 度量分析

1. 类图

2. 复杂度分析

    

  这次由于我的计算函数太复杂了,充斥大量if语句和for循环,复杂度就是灾难!而且里面有许多一模一样的语句,我应该将函数再统一化一下。

  对于Elevator类,复杂度太高的原因是因为它自己充斥了许多类似于CheckIfUpHasPeople这种private的检测函数。除了重构,减少elevator的功能,我目前想不到怎么避免这些。对于Control类,实际上没有什么复杂过程,但是还是有一些奇奇怪怪的函数,比如CanGetOnElevator。由于我设计的是能直达再上电梯、派活,所以从某种角度,这些函数是必需的。

3. 时序图

  我特意保留了elevator中的所有private函数,可以展示自己写的多么难看……我真不应该把几乎所有的功能都给电梯。我觉得我可以再设置一个电梯“小调度器”类(或者线程)。所以我这次的程序设计的很不平衡。

4. 设计原则

  • Single Responsibility Principle:电梯类设计冗杂。
  • Open Close Principle:三个电梯都相同,拓展性一般。
  • bug分析

  我这次是翻车作业。上次我提到了自己可能去maxf + 1,这回我修改了前面的方向判断函数,以为没问题了,但是还是有问题!因而强测发生了大型翻车事件。

  • 互测经验

   我发现了同学许多bug,超载的、没有结束的、线程调度有问题的……由于我写了评测机,我这次是黑箱测试,测试集是随机生成的。


 单元总结

  • 线程安全

  我在线程安全上遇到了许多问题,并且都只能凭借评测机测出:

1. [0.0]输入时线程陷入wait中

2. 读不到最后一组数据

3. 线程提前结束

奇妙的是,这些问题基本都可以靠sleep解决。但是我为了程序的安全性,在1中使用了读入延迟启动的方法。对于2、3,我新加入了一个end线程,解决了关闭过早的问题。

  • 设计原则

单一职责原则:没有一次作业能够做到……我写了太多的if和for循环判断结束、是否有人、去哪一楼层。

开闭原则:我在第一次作业就考虑到了后面的多电梯场景,所以程序的拓展性还是很强的。

  • 测试经验

  评测机是个must!感谢和我一起写评测机的两位优秀仙女puu和fuu!我们不光设计了全随机数据,还有针对性的压力测试和边界测试。我们尝试构造既有普遍性,还有针对性的测试集。相对于第一单元的针对WF的测试,这回我主要是根据功能。比如在第二次和第三次中,我都判断了一些奇怪的楼层和换乘。并且构造了充分的测试集(因为有可能4到3是对的但是2到3不对)。相比于一单元的无脑测试,这回的对于功能性的检测更加有挑战性。当然,这一单元的另一个重点是对于性能安全的测试。对于这个,我的方法是运行50组随机数据,如果错了那么我就提交数据。如果没错我就姑且认为性能是安全的。实际上,运行组数应该远远高于50组,但是我们写的评测机运行事件太长了,50组就要跑1小时。

原文地址:https://www.cnblogs.com/Wendy-Zheng/p/10738477.html