代码改变世界

oo第二单元总结

2019-04-23 17:45  gzhBuaa  阅读(183)  评论(0编辑  收藏  举报

设计策略

  本单元三次电梯作业涉及多线程的设计架构,需要格外注意多线程的协同、控制,保证线程安全。

  第一次电梯作业我采用了如下的架构,主要部分为:一个电梯类,负责从控制器中取请求;一个控制器类,负责接收乘客的请求,内置请求队列以实现FIFO调度;一个乘客类,负责从输入中构造请求并发送给控制器;一个结束信号类,负责接收由乘客发出的结束信号,电梯从中得知输入是否已经停止。除了主线程,本次作业有两个子线程:电梯线程,乘客线程。两个线程的共享资源为请求队列、结束信号,所以要保证两者的线程安全。

  第二次电梯作业的架构与第一次完全相同,不同之处在于控制器类、电梯类的内部行为更加复杂。电梯与控制器的交互行为有以下几种:没有乘客时,去队列中第一个乘客的所在楼层接乘客;开门之后,与控制器交互;开门交互之后,线程sleep400毫秒,在关门之前与控制器交互。每一次交互行为,电梯内部的请求池、乘客id数组以及目标楼层数组发生对应的变化。本次作业在线程设计方面与第一次作业一致。

  第三次电梯作业的复杂程度有了一个飞越。在架构方面,多了两个电梯类,总共有A、B、C三个电梯,三者的行为大致相同,但存在停靠楼层、运行速度等细节上的不同。在线程设计上,本次电梯作业有四个子线程:三个电梯线程,一个乘客线程。共享变量仍为请求队列和结束信号,对请求队列的任何操作:添加、获取请求,将请求拆分为两个请求,并加入对应的队列……都应该保证请求队列的线程安全。在类的内部行为上,本次作业更加复杂:电梯与控制器的每次交互,除了要进行第二次作业所涉及的所有操作,还要更改A、B、C电梯三者的请求队列。三者的请求队列又包括两种:可执行队列、阻塞队列。阻塞队列必须要等到可执行队列的请求执行完毕,乘客下电梯之后才可以被执行。这是针对换乘请求而特意设计的。

度量分析

第一次作业

UML类图

    

  第一次作业由于不太复杂,总体架构思路还是比较简单的。

方法度量

  度量工具好像出了一点问题,具体的方法名称没有显示。CC代表圈复杂度,衡量代码结构的复杂程度,反映程序设计的优劣。由于本次作业的难度较低,圈复杂度较小。说明本次的作业设计与代码实现的质量还是比较高的,不过电梯类内部的方法比较复杂,需要进行优化。

类度量

  比较直观的数据是NOM(类的方法个数)、LCOM(类的内聚缺乏度)。从数据看来我的类内部方法的设置还是比较合理的,内聚度较高。

UML协作图

 

  Commander、End类各实例化一个共享对象,这两个共享对象实现了电梯与乘客之间的交互。

第二次作业

UML类图

  第二次作业的架构与第一次作业的相同,不再多说。

方法度量

  本次作业的Elevator类圈复杂度较高,测试和维护难度较大,设计有待优化。

类度量

 

  本次作业Elevator类LCOM(聚合缺乏度)较高,方法内聚程度低,设置不太合理。

UML协作图

 

  与第一次作业相同。

第三次作业

UML类图

 

  本次作业比较复杂,尤其是控制器类,内部方法较多。

方法度量

   

  本次作业的设计较差,主要是为了保证正确性,在设计上暂时没有考虑到合理性和代码可维护性。对于CC度量指标,可以看到我的三个电梯类和控制器类都比较高,这反映了我在类内部的设计上存在着一些不足。类内部的参数较多,我对于这些参数的管理仅仅停留在功能实现上,没有考虑到方法耦合度之类的问题。

类度量

 

  之前已经说过我的类设计上的问题,在类度量中也可以发现这些问题。四个类的LCOM值都比较高,有待优化。

UML协作图

 

  在类的架构上,第三次作业相对于前两次作业而言,变化其实并不大。主要是电梯的数量发生变化。

BUG分析

  我的第一次、第二次作业没有出现bug。第三次作业的bug有以下两处:调度策略性能较差,导致部分测试点出现超时现象;电梯接乘客时,判断乘客是否已经进入电梯出现错误,导致乘客两次进入电梯。由于设计难度的增加,我的第三次作业质量并不好,在设计架构以及具体实现上都存在着一些问题。通过这三次作业,我明白了设计与测试的重要性。在设计时要保持清醒的头脑,在测试时要尽可能地构造完备的测试集,并且要有足够的耐心。

心得体会

  本单元电梯作业旨在训练我们的多线程编程能力,任务量较重。经过三个星期的不懈拼搏,我的多线程编程能力也有所提升,在多线程测试方面也有了一些自己的经验。从最开始的在概念上了解多线程编程,到逐步学习多线程编程知识,再到动手实践、写出比较安全高效的多线程程序,在本单元中我的收获颇丰。

  对于线程安全,我有以下几点心得:1、在设计时明确共享变量,理清线程的同步关系。这三次多线程作业的线程行为较为简单,共享变量也不多,所以在设计时可以很轻松地理清线程之间的关系、线程与变量之间的关系。2、善于用锁。无论是ReentrantLock类,还是synchronized方法,需要至少熟练掌握一种实现线程同步与互斥的方法,并弄清楚原理,进行比较深层次的学习,以确保我们可以正确地用锁。

  在设计原则方面,我有以下几点心得:1、线程类与服务类分离。例如设计电梯类,假设Elevator类内部设计了一些方法来实现电梯的行为,那么最好不要直接让Elevator类重写run方法并作为线程类运行,而是设计一个ElevatorThread类作为线程类运行,用来执行Elevator类的方法。这样设计可以更方便地理清我们的思路,并且有助于减小类的冗杂度。2、如无必要,勿增线程。每多设计一个线程,我们的设计与实现的负担就会加重一些。如果在设计时,发现某个线程类完全可以用普通的类来代替,那我们尽量设计成普通的类,将线程与线程之间的行为改为线程与该类的交互行为。这一点心得也许只适用于本单元,或者说只适用于多线程编程新手,旨在以正确地、安全地实现需求为主导,减轻设计、实现与测试的压力。