BUAA_OO第二单元反思与总结

BUAA_OO第二单元反思与总结

18375182 范竞元

第五次作业

架构分析

对于第五次作业,思考一种合理的架构显然没有第一单元的作业困难,对于多线程来说,几乎就在作业发布的同时我就决定采用黑板模式,我认为黑板模式的优点就是结构清晰,容易加锁,不易锁死,正确性比较容易保证。于是,我采用一个类当作“黑板”,这个类的核心是一个\(ArrayList\),储存着在各个楼层等待的人,那么针对这个容器的读或写操作都加上锁即可。因为此次只有一个电梯,所以我将“黑板”和控制类结合了起来,统称\(Controller\)类。关于电梯具体调度算法方面,我采取了\(Look\)策略,也就是电梯在最底层和最顶层之间运行。但当发现电梯所移动的方向上不再有请求时(包括上行和下行)立即改变运行方向。我认为这个电梯调度策略可以天然适配\(Night\)模式,因为按照\(Look\)策略的逻辑来看的话,电梯会运行到有人的最高层的位置后才会改变方向,这样可以在最短时间内接到最多的人。

在控制电梯方面,我将电梯的行为分成了很多小的行为,有进乘客,出乘客,移动一次,改变方向,检测是否\(wait\)和检测是否停止这几步,这使得我编程的思路极为清晰。

UML顺序图

对于第二单元作业来说,三次作业迭代下来架构基本上没变化,所以此UML顺序图可以适配整个第三单元。

类图分析

\(MainClass\)先初始化时间戳,然后创建一个\(Input\)实例,启动此线程。\(Input\)类负责将将要乘坐电梯的人写进“黑板”中,首先时实例化一个\(ControlCenter\)类作为控制电梯运行的类,然后启动此线程。\(Input\)类可以说是比较核心的类,它既担任了处理输入,将其存放到“黑板”中的角色,也同时存放着\(ControlCenter\)的实例,当输入结束后会改变\(ControlCenter\)的结束状态,当电梯和等待队列都没有人并且已经结束时,\(ControlCenter\)线程就会停止运行。对于电梯的具体运行的话,我是分了两个类写,其中一个类是\(Lift\)类,它只负责运行和储存乘客信息和自身状态,\(ControlCenter\)说什么它就做什么,本身不具有任何的逻辑。\(ControlCenter\)类负责具体实现\(Look\)策略和储存“黑板”。另外有一些比较细节的东西比如说电梯中只储存和自己运行方向一样的人,当电梯向上运行时,乘客的目的楼层是从小到大储存的,当电梯向下运行时,乘客的目的楼层是从大到小储存的。这样可以使得判断电梯是否要开门和乘客是否要出电梯更加方便。

复杂度分析

可以看出来,\(ControlCenter\)中的\(Look\)方法复杂度过高,我认为这是因为我储存即将要上电梯的乘客的数据结构还不够好,所以在执行\(Look\)策略时需要很多的循环和判断,因此此方法的复杂度过高。其他的方法比如说\(Morning,\ Night,\ Random\)等方法的复杂度也稍微高了点,我认为也是因为判断次数过多。

优缺点分析

优点

\(Look\)策略可以很好的适应各种人员的乘坐模式,并且此次作业所采用的黑板模式不容易出现Bug。

缺点

为了偷懒将“黑板”和\(ControlCenter\)类揉在了一起,导致第六,七次作业花了时间将其分离出来,组成了一个新的类。

第六次作业

架构分析

第六次作业相对于第五次作业的改动其实不是很大,主要工作都在于分离“黑板”和\(ControlCenter\)类,其中“黑板”就是\(WaitingQueue\)类,也就是储存着即将上电梯的乘客的容器,这次作业我将原来“黑板”中的一个容器换成了两个容器,都为\(ArrayList\),其中的\(Up\)储存着要上行的乘客,并且按照初始楼层从下到上储存,其中的\(Down\)储存着要下行的乘客,并且按照初始楼层从上到下储存。这样可以在执行\(Look\)策略时可以判断较少次。同时,因为黑板已经成为了一个单独的类,所以这次作业的架构更加的清晰了。为了开多个线程,我在\(Input\)类中建了一个储存\(ControlCenter\)类实例的容器,并且写了一个方法,让在容器中的实例循环开启线程。和第五次作业不同的是,这次作业需要有多个电梯同时运行,我将一个\(ControlCenter\)类和一个\(Elevator\)类视作一个电梯,\(ControlCenter\)类储存有\(Elevator\)类的实例。那么就需要有人员的分配算法,这次作业我采用的是自由竞争策略,比如说7楼有一个人想上14楼,那么三个电梯一起动,谁先接到这个人,这个人就归谁。

类图分析

这次作业和第五次作业的不同在于多了一个\(WaitingQueue\)类作为“黑板”,其他都完全一致。

复杂度分析

这次的复杂度因为对上次的架构进行了优化,所以复杂度不是很高,\(Look\)策略(\(changeDire\)中)的复杂度也有所下降。

优缺点分析

优点

\(Look\)策略可以很好的适应各种人员的乘坐模式。

自由竞争策略简洁好写。

进一步加强黑板模式的使用,使得架构更加清晰,\(Bug\)追溯更加方便。

缺点

自由竞争策略在极端条件下运行速度可能会很慢。可能导致超时。

第七次作业

架构分析

这次我仍然是沿用上次的架构进行更改,但是这次的电梯因为有停留楼层的限制,所以需要进行换乘,所以我稍微更改了一下\(Person\)类,\(Person\)类中原来只有\(fromFloor\)\(toFloor\)也就是乘客的起始楼层和目标楼层,但是我加入了一个\(middleFloor\)也就是中间层,当乘客上电梯时,会检测电梯是否能够将乘客运送到最终的目的地,如果不行的话就要设置中间层,获得乘客的目标层时,如果中间层为0,则目标层为最终层,如果中间层不为0,则目标层为中间层,当需要换乘的乘客第一次下电梯时,需要将中间层设置为0,\(fromFloor\)设置为原来中间层的值,然后重新进等待队列即可。但是我在做作业的时候遇到了一个很严重的问题就是无法停止程序,原来是检测如果此电梯和等待队列都没有人并且输入已经停止时,就停止运行电梯,但是这次因为引入了换乘机制,所以这个判断出现了一个漏洞,就是当别的电梯有换乘乘客的时候,其他电梯是不能够停止运行的,因为如果停止运行就会出现不能够把乘客送到目的地的情况。所以我在\(Input\)类中加了一个方法,是循环判断在运行的电梯中是否还有人。但是因为这个架构过于麻烦并且在不同的类中加锁太多,不能保证正确性,所以我就删除了换乘机制。另外,此次作业我还是继续使用自由竞争的策略。

类图分析

这次作业和上一次作业不同的地方在于要对电梯经过的楼层进行额外判断是否能够停下来。

复杂度分析

这次的作业因为要对每种电梯到达的楼层进行更多的判断,所以会有很多的\(if...else...\),因此在\(Look\)策略,\(getIn\)\(ifEmpty\)中的复杂度是要高一些的。

优缺点分析

优点

\(Look\)策略可以很好的适应各种人员的乘坐模式。

自由竞争策略简洁好写。

缺点

自由竞争策略在极端条件下运行速度可能会很慢。可能导致超时。

删除换乘机制也可能在极端条件下导致超时。

第二单元总结

这三次作业的难度虽然不是很难,但是对我来说也是一个新的挑战,因为之前并没有接触到任何的有关于多线程的编程问题,通过写这三次作业,我进一步认识到了设计模式和一个好的架构对于编写程序是有多么的重要,也使我更加深入地认识到了面向对象的设计方法。另外,在写第三次作业中,也使我认识到了线程安全的重要性。

和第一单元每次作业的推倒重建不同,这三次作业真正做到了基础的迭代开发,最开始设计的具有层次化架构的拓展性极好。上层完全不用考虑到底层的设计问题,比如说\(controller\)就完全不用考虑\(Elevator\)的具体状态,只需要发号施令即可。

针对线程安全问题,我采用了黑板模式,黑板是一个单独的类,因为首先要保证正确性,所以我直接将这个类中的所有方法都加上了锁,并没有考虑读写操作的不同。

至此,第二单元就结束了,希望能在后面的两个单元更加得心应手地编写程序。

posted @ 2021-04-26 09:29  hbhdfjy  阅读(76)  评论(0编辑  收藏  举报