OO第二单元总结

一、设计策略

1、第五次作业

第五次作业很自然的采用了生产者-消费者模式,设计了三个类:主类MainClass,托盘类Scheduler和电梯类Elevator,实现了两个线程:主线程和电梯线程,主线程接受乘客请求后通过putReq()方法将其加入Scheduler的队列,输入结束后通过setEnd()方法通知电梯没有更多请求,电梯每到达一层就通过getReq()方法从托盘中一次性取走所有请求放入自己的请求队列(关门的那一刻会再次调用该方法),而电梯的调度本人采用的是LOOK算法。

2、第六次作业

考虑到程序的迭代,在这次作业中总的结构没有变动,主要修改了以下部分:

1、主类MainClass,使得可以根据输入开启多个电梯线程;

2、Scheduler类的getReq()方法,增加了三个参数,请求人数(即电梯最大负载-电梯类人数-电梯自身请求队列的人数)、电梯所在楼层、电梯运行方向,这里没有急着将更多的乘客一次性装入电梯内部的请求队列(显然,局部请求队列的人数不计算在负载内),主要是这样多出的那些乘客多半会在一个电梯周期内得不到响应,所以本人决定让他们去请求其他的电梯,以实现公平的原则(事实证明,这样的效率只会更高且鲁棒性较好);而传入电梯当前所在层数和电梯运行方向主要是为了挑选出那些“顺路”的乘客(出发楼层和目的楼层都在当前电梯前进的方向上),但是事情往往没有想的这么完美,所以本人给出了条件较弱的选项:出发楼层在当前电梯前进的方向上,这样就可以尽量保证每个电梯都能取到最合适的乘客。

3、第七次作业

第七次作业继续迭代,改动主要有以下几个方面:

1、主类MainClass,使得可以根据指令新建电梯;

2、电梯类Elevator,新增题设的可变属性,比如类型、最大负载等,而且对于需换乘而尚未换乘的乘客,会在其下电梯时调用putReq()方法将其加入主请求队列;

3、设计了Path类,每个实例代表了一条乘客路径,包括出发电梯类型、中转楼层、达到电梯类型;

4、设计了TransableRequest类,其继承自PersonRequest类,主要新增了三个属性,需换乘标识符,已换乘标识符,可选换乘路径表;

5、修改了Scheduler类的getReq()方法,新增参数请求电梯的类型,而且在电梯在挑选乘客时,新增乘客对电梯类型需求的判断;

由于本人采用的是电梯自由竞争的方式,而且换乘路线比较固定,所以通过添加一些随机性来达到提升性能的效果,即每个乘客会根据电梯的竞争先后在可选换乘路径表中随机选择一条路径执行,通过最终的强测可以发现这种方式在有意减少总运行时间的同时,也无意间减少了乘客的平均等待时间。

小结

这个单元的三次作业均是采用生产者-消费者的模式,所以在线程的同步和互斥控制上也较为容易,主要是通过给Scheduler类的putReq()方法和getReq()方法加锁实现的,但是现在细细想来自己好像并没有将Scheduler类设计成一个线程安全类,所以在电梯线程里还进行了额外的加锁。而在同步控制中花费了我更多精力的应该是如何结束电梯线程,特别是第七次作业,我不得不记录每一个需换乘乘客的加入和每个需换乘乘客的完成换乘,再结合scheduler类中的输入终止信号end,来完成整个终止线程的工作。

 

二、程序结构分析

由于三次作业是迭代完成,故以第七次作业为例。

OO第二单元总结_第1张图片

OO第二单元总结_第2张图片

OO第二单元总结_第3张图片

OO第二单元总结_第4张图片

可以发现通过两次迭代,getReq()方法与电梯线程的耦合度不断加大,且代码的质量逐渐变低,说实话要是继续增加要求,恐怕代码愈加难以维护,测试难度也会不断加大(实际上,在第三次作业中,为了debug,本人设置了很多输出语句才得以定位),而在电梯类中,由于乘客要求不断复杂,peopleIn(),peopleOut()和getDirection()方法的判断语句不断复杂,降低了代码的质量。

三、基于SOLID的设计分析

1、SRP原则

大体来说就是每个类或方法都有自己明确的职责,各司其职以实现高内聚,低耦合。在第七次作业中,我虽然自认为将各类的功能分的很明确,但是通过上面的程序结构分析可以看到电梯线程和scheduler类的耦合较为严重,这可能是因为scheduler类的getReq()方法接收了太多和电梯线程有关的参数,现在想来可以按照理论课上提及的方法,设计一个消息发布板,电梯将自身的属性信息送入其中,getReq()方法再从中直接获取即可。

2、OCP原则

这个大概的意思就是每次迭代的时候不用改已写的部分,只需添加新增功能即可。这点在我的两次迭代中实现的较好,很多代码从始至终都没有改过,修改的部分都集中在个别方法和类,但认真来说的话,我这还不是真正意义上的OCP,毕竟各种判断语句我在不断修改,所以我的代码架构还不够好,若还要继续扩展的话可能代码会逐渐臃肿和杂乱。

3、LSP原则

这在我第七次作业中深有体会,由于新增了继承于PersonRequest类的TransableRequest类,当时还以为会大改一番,但是最后发现将所需地方的PersonRequest类换为TransableRequest类后刚好能满足功能需求,所以在这点的架构上还是算比较成功。

4、ISP原则

第七次作业中使用的接口就只有Runnable接口,所以也不会有什么关于接口隔离的问题。

5、DIP原则

第七次作业的层次关系主要体现在继承上,但没有出现依赖倒置的问题,而且我感觉出现这种问题的架构实在不很常见。

四、bug分析

1、自身bug

这三次作业中只有第五次出现了一个bug,错误原因是电梯线程提前结束,虽然没有在本地复现出这个bug,但是通过阅读代码,分析各种线程先后执行的情况,最终锁定了bug的位置。

2、hack策略

想要hack别人,至少首先要实现根据指定时间输入,关于这点,我采用评论区的方法,利用python中的subprocess包和time包实现,通过txt文件读取数据,并输出到txt文件中。

前两次作业的输出检查还比较好实现,但最后一次情况比较复杂,本人没有实现。

在hack别人时前由于两次可以自动检查,所以采用自动测评的方式来hack,但是可能是因为生成的数据没有涵盖的范围不够,没有找到别人的bug;d第七次作业时则是采用手动构造数据的方式(感觉随机生成的数据不好检查输出),主要设计了涉及换乘、增加电梯、请求一次性输入等情况的样例,但遗憾的是仍没有找到bug。

五、心得体会

本单元相对于上一单元最大的成功就是实现了迭代开发,但是又出现了许多新的问题:

1、采用的设计模式比较单一,仅仅只有生产者-消费者模式;采用的同步手段也较为简单直接,只尝试了synchronized,而感觉更灵活的Lock会更适用于这几次作业

2、在最初的架构设计中没有为后来的迭代多加考虑,这几次的迭代有点运气成分,所以最后得到的迭代结果有些臃肿,耦合度也比较高

3、自己设计的自动评测机效果一直不是很理想,而且只能一个一个样例来跑,不能充分利用电脑的多核结构,使得评测效率很低

在今后的作业中最主要的还是要优化代码架构,尝试更多的设计模式,使得迭代更加自然,把耦合度降下来。

你可能感兴趣的:(OO第二单元总结)