OO第二单元总结

OO第二次博客作业

一、从多线程的协同和同步控制方面,分析和总结自己三次作业的设计策略。

1.第一次作业

第一次作业是最简单的傻瓜调度,按照先来先服务的原则,并且没有捎带原则。架构主要是两个线程和一个调度器。两个线程分别是输入线程和运行线程。输入线程将输入的乘客请求信息送入调度器队列,而电梯则从调度器中取出请求并执行。对于程序结束方面,当输入线程读入null时,给调度器一个信号,调度器再判断队列是否为空,如果为空,给电梯线程传递信号,电梯判断乘客是否为空,如果为空则结束。

2.第二次作业

第二次作业加入了捎带机制,我的调度策略是稍微改进的ALS调度策略,当电梯里没有乘客时,选取请求队列的第一个请求作为主请求,如果电梯里有乘客时,选取距离当前电梯最远的乘客作为主请求,以此来做到最多的捎带。架构方面与第一次电梯类似。

3.第三次作业

第三次作业变成了多电梯问题,复杂度也有所提升。在调度策略方面,我还是沿用了第二次作业的方案,以至于性能分不够高。架构方面,依旧是有电梯运行线程和输入线程,不同的是,我在电梯内部增加了自己的请求队列,由于调度器的复杂性,我将调度器分为了主请求队列和管理调度策略的elevatorManager,并且建立了新的调度线程。我新建了一个Person类,属性包括id,from,to,currentElevator等,便于调度策略的执行。对于请求的分配,我采取的方法是,假设若干电梯都能送达,则判断是否有电梯满员,如果满员,则选取剩下的电梯,再在不满员的电梯内选择默认顺序分配。
对于换乘问题,假设person需要乘坐A,C电梯到达目的地,换乘楼层是transferFloor我的方法是,在调度器中将Person的currentElevator设置为A电梯,把目的地设为transferFloor,在Elevator中,当Person出电梯时判断是否需要换乘,如果要换乘,将currentElevator设为null,再将from设为transferFloor,目的地还回去即可。而这种做法还需要保证在电梯运行遍历电梯内的请求队列时,如果请求的currentElevator不是this且不是null就跳过不执行,而去寻求其他请求。
下图是我第三次作业的架构图

二、基于度量来分析自己的程序结构度量类的属性个数、方法个数、每个方法规模、每个方法的控制分支数目、类总代码规模计算经典的OO度量画出自己作业的类图,并自我点评优点和缺点,要结合类图做分析通过UML的协作图(sequence diagram)来展示线程之间的协作关系(别忘记主线程)从设计原则检查角度,检查自己的设计,并按照SOLID列出所存在的问题

1.第一次作业

(1)类图

(2)类复杂度分析

(3)UML协作图

总的来说,第一次作业是非常简单的了,目的大概是为了让我们熟悉多线程。由于简单,我也没有创建完整的调度器,而是通过一个简易“调度器”—请求队列来调度请求。类之间分离比较明显,复杂度也不高。

1.第二次作业

(1)类图

(2)类复杂度分析

(3)UML协作图

第二次作业,大部分沿袭了上一次作业的架构,但是不同是增加了一个ElevatorManager的调度器,但是由于是一部电梯,调度器的功能也可有可无,原本我是想为第三次作业做铺垫,但是由于当时理解不深入,能力有限,我只是简单地将线程的创建拉了出来,大多数的功能还是电梯自己执行。除此之外,个别方法的复杂度有点高,对没错,就是本该从电梯中分离出来的在队列中寻找乘客的方法。

1.第三次作业

(1)类图

(2)类复杂度分析

(3)UML协作图

第三次作业的复杂程度比之前两次大了不少,虽然多亏前两次的铺垫,第三次作业花的时间较少,但是在架构上还是有不小的问题,比如,建造类中类,导致类耦合度升高,电梯类和调度器的功能没有彻底地分离,代码的复杂度较高。

SOLID原则

单一责任原则(SRP)
>当需要修改某个类的时候原因有且只有一个(THERE SHOULD NEVER BE MORE THAN ONE REASON FOR A CLASS TO CHANGE)。换句话说就是让一个类只做一种类型责任,当这个类需要承当其他类型的责任的时候,就需要分解这个类。 

开放/封闭原则(OCP)
>软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。这个原则是诸多面向对象编程原则中最抽象、最难理解的一个。

里氏代换原则(LSP)
>当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-a关系

接口分离原则(ISP)
>不能强迫用户去依赖那些他们不使用的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好。

依赖反转原则(DIP)
>1. 高层模块不应该依赖于低层模块,二者都应该依赖于抽象 2. 抽象不应该依赖于细节,细节应该依赖于抽象 

本次作业中,重点考虑单一责任原则和开放/封闭原则。
对于单一责任原则,我认为三次作业的Elevator类都有一定程度的违反该原则,在前两次作业中,Elevator类在运行过程中会寻找请求队列中的乘客,从而选择是否搭载以及如何运行,我认为这一点应该交由调度器执行。对于第三次作业,为了方便,我直接将三个请求队列类放入了电梯,够造了类内类,我想,这一点很不面向对象,也违反了SRP原则。
对于OCP原则,也是同样,由于我将Elevator的运行状态直接交由其自身管理,因此在替换调度策略时,我还要修改部分Elevator的代码,因此没有遵守开放封闭原则。

三、分析自己程序的bug分析未通过的公测用例和被互测发现的bug:特征、问题所在的类和方法特别注意分析哪些问题与线程安全相关关联分析bug位置与设计结构之间的相关性

我的两次bug都是在测试结束时发现的。
第一次在输入终止以及任务完成后程序并没有结束。在寻找bug时利用了print大法,最后发现是Elevator线程没有结束,问题发生在我对synchronized理解实在是不够。刚开始我在Elevator线程中添加了wait(),而期望在Request线程中的notifyAll()唤醒它,后来通过Udine课件中消费者生产者模式的学习以及对synchronized的更深的理解,终于解决了问题,我最后把wait和notify都放在了调度器里,利用对调度器的push与poll进行对线程的wait和notify操作。
第二次的bug是在做第三次作业时,由于第三次作业是直接对第二次作业的修改,而第二次作业,我利用了单例模式,调度器的部分属性添加了static关键字,而第三次作业忘记修改,导致电梯总是提前结束,我同样利用print大法找到问题并且进行修改。

四、发现别人bug时所采取的策略

对于本单元作业,找到别人的bug其实算是比较难的事情,而我也没有指望通过找bug去取得分数,我选择了去拜读同组的代码,也学到了一些东西,比如说第三次作业利用Person类去存请求也是我在拜读别人的代码才学到的。

心得体会

多线程编程是java非常重要的一个特性,而对于多线程编程,我认为最难的就是同步与互斥的内容以及设计模式的掌握。在本单元的作业中,也只是简单的练习了相关的内容,我认为要学习的东西还有很多,希望可以加深这方面的学习。

posted @ 2019-04-24 00:23  hbboy  阅读(168)  评论(0)    收藏  举报