OO第二次博客作业

OO第二次博客作业

 

一、分析和总结自己三次作业的设计策略

1、第一次电梯作业

电梯第一次作业是按照课上所发的PPT的架构来的,按照生产者消费者的模式设计,将电梯作为消费者,输入作为生产者,将等候队列作为托盘,这样的架构非常清晰,共享数据就是托盘中的等候队列,所以要加锁的方法就是对该等候队列做一些操作的时候。主线程用于将输入线程和电梯线程启动。这其中有一个难点,就是怎么判断电梯停止,因为有可能电梯载人的任务还没有结束而此时又没有人,所以电梯不能停止只能等待;而也有可能是在输入结束而电梯还没有将人运完就发出了结束信号,此时还要将电梯里的人运完以后电梯再停止。所以电梯判断条件不只是结束信号,还有电梯里的人和请求队列里的人都为空的时候,才能真正结束电梯的运行。

2、第二次电梯作业

电梯第二次作业是捎带的实现,这次的作业我依然采用第一次作业的架构,将调度的任务交给了电梯,输入只负责将请求加入队列,队列只负责更新队列和返回一些信息。在电梯的调度中,最重要的就是捎带请求的实现,要注意主请求和附加请求,还要注意过程中捎带的请求的实现。

3、第三次电梯作业

电梯第三次作业是实现了多部电梯的协作配合,每部电梯的运行时间、最大载客量、停靠楼层都不一样。这一次我依然使用了前两次的框架,不过由于需求的不同,每个类的方法有些许的不同。输入线程依旧只将请求加入到队列中并且返回请求结束的标志。队列由三个请求队列组成,相当于三部电梯的等候队列,除此之外还有请求是否终止的信号和换乘人员是否已经全部换乘这两个数据成员,方法就是更新队列和返回一些重要信息,还有一个重要的方法就是将请求根据请求中的一些信息放到合适的等候队列中。电梯依旧采用第二次中的捎带算法,三部电梯放在一起统一建模。然后我增加了一个person类用于存储请求、请求要进入的电梯类型、换乘信息等有关请求的重要信息的类,每次得到一个请求之后和将请求加入到队列中之前构造一个person类的对象,然后将这个对象加入到队列中。主线程用于启动三部电梯和输入线程。电梯运行的结束仍然使用第一次作业的方法。我的各个类的耦合程度比较高,尤其在每个电梯内,与队列和person类耦合程度很高。

 

三次作业的输入线程和电梯线程都是同时启动,第三次作业更是三部电梯和输入线程一起启动,每个线程有自己的任务完成,输入线程将请求加入到队列中,然后由三部电梯完成请求,队列就是共享数据,通过队列相关方法的加锁实现了各个线程的配合不会出错。

二、基于度量来分析自己的程序结构

1、第一次电梯作业

(1)方法复杂度、类复杂度度量

(2)代码行数统计

(3)类图及分析

分析:

缺点:类与类之间的依赖关系(dependency)太多,类之间的耦合程度太高,不利于扩展

优点:线程之间的协作关系比较清晰,类的各自任务很清楚

(4)sequence diagram(时序图)

主线程与其他线程的关系:

 

Input和Queue之间的时序关系:

Elevator和Queue之间的时序关系:

(5)SOLID检查

S:Queue类的职责比较多,而且与Elevator类的耦合很多

O:尤其是Elevator类太过于具体化,导致继承扩展都很困难

L:没有出现继承,但是我的Queue和Elevator类都不太适合继承

I:没有实现接口

D:模块之间上下层次不明显

 

2、第二次电梯作业

(1)方法复杂度、类复杂度度量

(2)代码行数统计

(3)类图及分析

分析:

缺点:有些类的方法和数据成员太多了,例如Elevator类和Queue类,而有些类十分简单,如Main类,分配十分不均

优点:类之间的耦合程度不是非常高,每个类各自的任务比较清晰

(4)sequence diagram(时序图)

 主线程与其他线程的关系:

输入线程和Queue的关系:

Elevator线程和Queue之间的关系:

 

5)SOLID检查

S:Elevator和Queue两个类的方法和职责太多,而且之间交互太多,可能会导致逻辑难以封闭

O:Elevator类的run方法实现太复杂,太过于具体,非常不适合继承

L:没有写子类,但是各个类的实现太过于具体,不适合继承

I:没有接口

D:模块上下层次不明显

 

3、第三次电梯作业

(1)方法复杂度、类复杂度度量

(2)代码行数统计

(3)类图及分析

分析:

缺点:依然是分配不均,有些类的方法和数据成员太多,像Person类,其实如果Person类中PersonRequest类中一些数据成员可以公开的话,Person中的数据成员可以少一些;类之间的交互太多太频繁

优点:每个类的职责比较清晰,层次关系比较明显

(4)sequence diagram(时序图)

主线程与其他线程的关系:

Elevator和Person、Queue之间的关系:

Person和Queue之间的关系:

(5)SOLID检查

S:三个类(Person、Elevator、Queue)的方法太多,Elevator的run方法有点面向过程的感觉

O:Person将PersonRequest类包含其中,但是我忘记可以使用继承的方法,没有通过扩展而是通过包含,代码复用性不强

L:没有注意实现PersonRequest类的子类

I:没有实现接口

D:没有仔细考虑类之间的继承关系

 

三、分析自己程序的bug

1、第一次电梯作业

第一次电梯作业开始的时候交上去一个点都过不了,后来有同学在群里提示了,我才意识到,当输入发出终止信号的时候,请求有可能还没有执行完,这个时候电梯不能停止,所以电梯的停止信号是输入发出了并且请求队列中请求数为0。

2、第二次电梯作业

 第二次电梯作业我的捎带实现的不完全,一个重要的捎带情景没有实现,代码写的很乱,就比较难改。没有实现的是在实现主请求的过程中捎带中途上来的乘客。运行程序没有出错,但是由于没有实现运行过程的捎带,所以运行时间超时。现在想起来,我应该在run中按以下过程实现:首先要在所有请求中找到主请求,然后确定电梯运行方向,接着判断电梯在完成主请求之前是否有附加请求要出电梯或者有请求要捎带。

3、第三次电梯作业

 第三次作业我吸取了第二次作业的教训,同时也沿用了第二次作业的架构,在其基础上增加了一些方法、类以实现新的任务。bug在强测和互测中都没有找到,但是我知道这不代表我的程序没有bug。我的程序没有优化,跑得很慢,我觉得这个也算是一种bug吧,毕竟只是完成了指定的任务,而没有考虑到性能。

 

四、分析自己发现别人程序bug所采用的策略

其实这几次作业的bug我不太会找,我自己不会搭评测机,没法做大数据集的测试,而且多线程的错误有的时候难以复现,这就使得测出bug更加有难度。我只能在我认为可能出错的地方设置一些数据,对程序进行测试。

对于线程安全问题,我觉得主要就是就是共享数据的使用,看别人代码的时候注意共享数据的方法是否上锁,是否互斥,我只使用了synchronized这个关键词来实现互斥。大家基本上都不会在这里出错。

 

 与第一单元不同的是,多线程的bug不太容易复现,而且调试的时候Debug基本上没办法用,使用输出调试最方便定位bug。

 

五、心得体会

首先讲一下我这个单元的感受,我意识到虽然我做不到优化,但是基础的任务完成还是可以的,所以我对于我第二次作业因为超时而没有交上去感到非常后悔,我时间没有安排好,然后后来写的时候就比较匆忙,加上思考不完善,写的代码思路不清晰,所以没能够完成第二次的任务。我觉得要留足够多的时间给OO的作业,之前一直是只想要完成任务,但是如果时间充足,自己多加思考还是可以做至少一点简单的优化的。在完成作业的过程中,不仅要专注于题目本身,还要多看看书上关于多线程的讲解内容,因为不能只有输出没有输入,这样做完作业后还是得不到更多的提升,顶多只是知道了怎么完成这次的作业,可能关于自己的程序的优化空间都不知道在哪里。希望下一单元可以好好完成。

线程安全方面,共享数据要注意互斥的实现,不然会造成错误,使用好synchronized。

设计原则方面,要注意各个类自己的职能,使用面向对象的思维方式,解析出每个对象中的信息;各个类之间的交互尽量少,减少耦合。

posted @ 2019-04-24 19:58  17373183  阅读(130)  评论(1编辑  收藏  举报