北航oo作业第二单元小结

类的设计:

    首先,我对我的思路进行整体的说明,由于我的三次作业,思路是继承的,所以做总体的说明

    第一,   Main类,Main类自身并没有功能,他的功能只是构造需要的电梯线程和输入线程。

      其中,第三次作业中,main类负责将电梯参数(运转时间,负载上限,运行楼层)传入Memory类和Elevator类

    第二,   是Eleinput类,这个类,是一个单独的线程,功能是读入需求,每次读入需求,将其写入Memory类中,当读入null时进程结束。

      输入器,与电梯类不相连,只负责传数据给Memory,设计安全。

    第三,   是Memory类,Memory类内构造了一个线程安全的容器(阻塞对列,或concurrenthashmap),功能是:

      存储需求,

      反馈自身一部分状态(如是否有需求,以及传出需求),

      删除已执行的需求,

      在第三次作业里,还加入了对换乘的处理。

    第四,   是Elevator类,每次从Memory与该电梯内部队列(即已经进入电梯的乘客)中选择一个主需求,执行该需求的过程中,逐层(如果能到达的话,判断上客,下客。

      我的设计中,电梯无法直接访问需求队列,只能通过memory中的mainset方法和search方法,得到一个需求,这样设计安全,也能大大解耦。

    第五,   是PersonRe类,这个类,是官方给的包的personrequest的拓展,在这个类中实现了,改写起始楼层的功能。在程序中,用该类,代替personrequest(第三次作业中使用。

    时序图如下:

 

  1. 第一次作业

    由于,第一次作业是先来先服务的傻瓜电梯,我并没有使用算法来优化电梯的时间。

    在整体的设计思路上,设计了两个独立的线程,电梯线程与输入请求模拟器线程。在两个电梯的通讯中,由于首次设计多线程程序,并且第一次作业没有限制CPU时间,我选择了通过while循环暴力轮询的形式。

    在两个线程之外,通过阻塞队列,设计了一个调度器,调度器,从输入请求模拟器中读入请求,并将其存入到调度器内的队列中,电梯通过while循环轮询调度器内是否有请求,一旦有请求就从阻塞队列中移出一个请求,并存入电梯内,循环这个过程即可。

    最后,结束进程,通过两个条件判断,第一,在输入请求模拟器线程结束时,将调度器内内置的bool值设为true,当该值,与调度器内队列isempty的bool值都为true时,结束进程。

    代码分析:

 

     由类图可见,我的两个线程只是简单的一写一读。层次简单。

    下面是我的代码度量分析:

    这一系列的作业,我已经开始掌握了oo思想,没有出现上一系列的作业那种复杂度几十甚至上百的情况,不过run方法中,还是使用了过多的if判断,这一部分,可以选择分成三个小类,这样的话会是更好的选择。

  2. 第二次作业

    第二次作业是同向请求可稍带的“ALS电梯”。设计思路,与第一次作业有变化。首先,本次电梯,是需要考虑捎带的,而不是每次只服务第一位来的人。所以调度器内存储数据的容器不能继续使用阻塞队列,应该使用hashmap。在hashmap中,将需求到来排序的sequence作为key,需求作为value,这样也方便选择最先来的乘客进行服务。

    此外,为了限制CPU时间避免暴力轮询,我使用了wait--notify让线程在不需运行的时候wait,具体的实现方法,是选择waitElevator类,每当Eleinput类,读入新的需求,便notifyall一次,最后,为了线程安全结束,当Eleinput类结束时,也notifyall一次。

    最后,为了使电梯实现捎带功能,在电梯设计中,加入乘客处理类,每到一层后,先判断该层是否有人下,再结合电梯运行方向判断是否有乘客进入电梯,sleep开关门时间后,再判断一次是否可以捎带电梯(这一点十分重要,如果没有这一次判断,会使得可以上电梯的人无法被捎带,如果只有这一次判断,那么会导致每一层都需要开关门时间,尽管可能该层不需要停靠开关门)。

    类图分析:

     可以发现,代码架构变化不大,只是在电梯内加入了部分方法。

    代码度量分析:

Method

ev(G)

iv(G)

v(G)

EleInput.EleInput(Memory)

1

1

1

EleInput.run()

3

3

3

Elevator.Elevator(Memory)

1

1

1

Elevator.choosemain()

4

3

4

Elevator.everyfloor(int,PersonRequest,int)

1

5

5

Elevator.floordeal(int,int,int,int)

1

2

5

Elevator.minus(int,int)

2

1

2

Elevator.order(int,int,int)

4

1

4

Elevator.pepolein(int,int,boolean)

1

3

3

Elevator.pepoleout(int,int)

1

4

4

Elevator.run()

5

10

12

Elevator.searchele(PersonRequest)

3

2

3

Elevator.sleeptime(long)

1

2

2

Memory.Mainisset()

2

1

2

Memory.Mainset()

3

2

3

Memory.Memoryadd(PersonRequest)

1

1

1

Memory.Memoryisempty()

2

1

2

Memory.Search(Integer)

1

1

1

Memory.Stopout()

1

1

1

Memory.follow(int,boolean)

5

4

5

Memory.order(int,int)

2

1

2

Memory.setStop(boolean)

1

1

1

Work.main(String[])

1

1

1

       

Class

OCavg

WMC

 

EleInput

2

4

 

Elevator

3.91

43

 

Memory

2

18

 

Work

1

1

 
       

Package

v(G)avg

v(G)tot

 
 

2.96

68

 
       

Module

v(G)avg

v(G)tot

 

ElevatorPro

2.96

68

 
       

Project

v(G)avg

v(G)tot

 

project

2.96

68

 

    由于我基本继承了第一次作业的写法,Elevator中的run方法逻辑复杂度还是过高,order、choosemain等方法,也是因为if等判断过多,带来了逻辑复杂度过高,这是我程序设计的弊端。此外,本次作业中,由于run方法内,每层判断能否捎带乘客,带来了过高的模块设计复杂度和圈复杂度。在debug过程也因此耗费了很多精力。

3. 第三次作业

    第三次作业,基本是在第二次作业的基础上完成,只做了为数不多的改动。

    第一,   将personrequest拓展成一个新类,实现改变出发地点的功能。

    第二,   为电梯添加负载人数等接口。

    第三,   将调度器中的hashmap加锁。

    第四,   在调度器中加入换乘类,计算请求是否需要换乘。

        如果需要换乘(即三部电梯都不能独立完成,则将该电梯的转乘位设为由换乘类算出的最佳换乘楼层)。当有电梯到达该需求出发楼层,且能到达换乘楼层的就进行捎带。

    第五,   在电梯内加入换乘功能。

        判断乘车出电梯,加入换乘的情况,那么乘客到达换乘楼层,就出电梯,出电梯时,还要调Memory中一个特殊的方法,将这个需求处理后添加到请求队列中,(起始楼层更改,转乘位置为0),并notifyall一次。

     类图:

    本次类图中可以看出,加入了新类(数据类)PersonRe,该类,由Memory类处理完成。

    度量分析:

Method

ev(G)

iv(G)

v(G)

Method

1

1

1

Method

3

3

3

Elevator.Elevator(Memory,String,List,long,long,long,int)

1

1

1

Elevator.checkfloor(int)

3

2

3

Elevator.choosemain(String,Boolean)

9

6

9

Elevator.everyfloor(int,PersonRe,int)

1

5

5

Elevator.floordeal(int,int,int,int)

1

2

5

Elevator.order(int,int,int)

4

1

4

Elevator.pepolein(int,int,boolean)

3

4

5

Elevator.pepoleout(int,int)

1

10

11

Elevator.run()

7

11

13

Elevator.searchele(PersonRe)

3

2

3

Elevator.sleeptime(long)

1

2

2

Elevator.tofloor(PersonRe)

4

3

4

Elevator.waituntil()

3

3

4

Memory.EleTrans()

2

1

2

Memory.Mainset(String)

9

9

9

Memory.Memoryadd(PersonRequest)

1

1

2

Memory.Memoryaddextra(PersonRe)

1

1

1

Memory.Memoryisempty()

2

1

2

Memory.Search(Integer)

1

2

2

Memory.Stopout()

1

1

1

Memory.check(String,PersonRe)

1

3

3

Memory.checktrans(PersonRe)

2

2

4

Memory.follow(int,boolean,String)

6

4

6

Memory.listrtans(PersonRe,List)

3

2

5

Memory.order(int,int)

2

1

2

Memory.setStop(boolean)

1

1

1

PersonRe.getDirty()

1

1

1

PersonRe.getFromFloor()

1

1

1

PersonRe.getPersonId()

1

1

1

PersonRe.getToFloor()

1

1

1

PersonRe.getTrans()

1

1

1

PersonRe.resetDirty()

1

1

1

PersonRe.setDirty()

1

1

1

PersonRe.setFrom(int)

1

1

1

PersonRe.setMemory(PersonRequest)

1

1

1

PersonRe.setTo(int)

1

1

1

PersonRe.setTrans(int)

1

1

1

Work.main(String[])

1

1

1

       

Class

OCavg

WMC

 

EleInput

2

4

 

Elevator

5.15

67

 

Memory

2.92

38

 

PersonRe

1

11

 

Work

1

1

 
       

Package

v(G)avg

v(G)tot

 
 

3.12

125

 
       

Module

v(G)avg

v(G)tot

 

ElevatorPlus

3.12

125

 
       

Project

v(G)avg

v(G)tot

 

project

3.12

125

 

    其中,逻辑复杂度,是和前两次作业一样由if判断导致。而在pepoleout类和mainset类中的模块设计复杂度增加的原因,是我加入了换乘。我的换乘实现方式需要Memory和Elevator的协作处理,导致该复杂度增加。至于处理方法,可以添加一个新的类,在将调度器的存储需求队列的功能分离,可以解耦。

自己程序的bug:

    我只在第三次作业出现了bug,该bug来自于在程序中使用固定值,进行for循环,由于第二次电梯最多30条指令,我将该固定值设计为32,在第三次电梯中沿用了第二次电梯的设计,导致,我的电梯,无法处理32号之后的指令。

    该结构来自于结构的不合理,结构中应避免出现魔术数字。

发现别人的bug

    本次系列课程中,我未能发现别人的bug,但我采取的策略是构造测试样例,并带入检验。

    构造测试样例,重点考察,换乘情况,一上一下,以及大量人在同一楼层同时进入。

心得体会:

    第一,在程序设计中,一定要避免magic number的引用!我第三次作业就是血泪的教训。

    第二,方法一定要简洁,一个方法只做一件事,这样能降低逻辑复杂度,降低debug难度。

    第三,要使用wait和notify机制,用好这个机制,可以减少cpu时间,写出巧妙的代码,(跑测试样例时,也不会像暴力轮询那样,风扇狂转)

    第四,多花时间进行架构的设计,本次电梯作业中,如果第一次就做出了非常漂亮的架构,整个扩展过程都会舒服很多,程序的稳定性也会更佳。

    第五,在synchronized的运用,要多加思考,用少了会影响正确性,用多了又影响性能,需要精雕细琢。

posted @ 2019-04-23 23:02  溪阶砧  阅读(315)  评论(0编辑  收藏  举报