OO第二单元——电梯调度——总结
第二单元的OO就要结束了,在此,我来总结一下自己OO第一单元的情况。
总览
第二单元有三次作业,分别为:傻瓜调度单电梯(AWL),带捎带的单电梯,多电梯。其中,上面说的调度方法只是一个最低限度,我们可以写更优的调度方法。
我整体采用了调度器向电梯发指令的结构,即电梯是一个模拟真是电梯轿厢的线程(命名为Car),这个car类似于真实的电梯,只能接受调度器/控制器的指令上下、开关门,不关心是否有人,有多少人,怎么走。
一下是我各次作业的架构。
第一次作业
整体架构
第一次作业我简单的分为读入、调度器、电梯三个线程
类图如下:

| class | OCavg | WMC |
|---|---|---|
| UserInterface | 3.0 | 3.0 |
| MainClass | 1.0 | 1.0 |
| InstrCache | 1.6666666666666667 | 5.0 |
| Instr | 2.142857142857143 | 15.0 |
| Controllor | 2.3333333333333335 | 14.0 |
| CarDoorWrongException | 1.0 | 1.0 |
| Car | 2.090909090909091 | 23.0 |
| Total | 62.0 | |
| Average | 2.066666666666667 | 8.857142857142858 |
| Project | AHF | AIF | CF | MHF | MIF | PF |
|---|---|---|---|---|---|---|
| project | 85.71% | 57.14% | 52.38% | 16.67% | 0.00% | 100.00% |
| Class | CBO | DIT | LCOM | NOC | RFC | WMC |
|---|---|---|---|---|---|---|
| Car | 5 | 2 | 1 | 0 | 22 | 23 |
| CarDoorWrongException | 1 | 3 | 0 | 0 | 2 | 1 |
| Controllor | 5 | 2 | 2 | 0 | 22 | 14 |
| Instr | 3 | 1 | 3 | 0 | 12 | 15 |
| InstrCache | 3 | 1 | 1 | 0 | 6 | 5 |
| MainClass | 3 | 1 | 1 | 0 | 6 | 1 |
| UserInterface | 2 | 2 | 1 | 0 | 6 | 3 |
这次作业比较简单,是为了熟悉多线程,耦合度不高

第二次作业
第二次作业,主要改造了第一次作业的Controller,使其支持新的算法。
控制器中,我采用了look算法,使其可以捎带。

| Class | OCavg | WMC |
|---|---|---|
| Car | 2.14 | 30 |
| CarDoorWrongException | 1 | 1 |
| Controllor | 4 | 48 |
| FloorMap | 2.5 | 5 |
| Instr | 2.17 | 13 |
| InstrCache | 1.5 | 9 |
| MainClass | 1 | 1 |
| Request | 1.57 | 11 |
| UserInterface | 3 | 3 |
| Project | AHF | AIF | CF | MHF | MIF | PF |
|---|---|---|---|---|---|---|
| project | 82.14% | 50.00% | 38.89% | 24.00% | 0.00% | 100.00% |
| Class | CBO | DIT | LCOM | NOC | RFC | WMC |
|---|---|---|---|---|---|---|
| Car | 5 | 2 | 1 | 0 | 26 | 30 |
| CarDoorWrongException | 1 | 3 | 0 | 0 | 2 | 1 |
| Controllor | 6 | 2 | 2 | 0 | 38 | 48 |
| FloorMap | 1 | 1 | 1 | 0 | 2 | 5 |
| Instr | 3 | 1 | 2 | 0 | 7 | 13 |
| InstrCache | 3 | 1 | 1 | 0 | 9 | 9 |
| MainClass | 3 | 1 | 1 | 0 | 6 | 1 |
| Request | 2 | 1 | 2 | 0 | 11 | 11 |
| UserInterface | 3 | 2 | 1 | 0 | 7 | 3 |
这次作业基本和上次不变,这得益于我在上次作业中考虑了拓展性,因此拓展性是十分重要的。

第三次作业
第三次作业,由于加入了多电梯,请求拆分,我的结构有了较大的改动。
一开始我的打算是每个人开一个线程的,自行去决定自己怎么行动。之后发现这样不是很好,于是还是改成了读入——控制器——电梯三线程。

| Class | OCavg | WMC |
|---|---|---|
| Car | 2 | 32 |
| CarDoorWrongException | 1 | 1 |
| Controllor | 3.87 | 58 |
| ElevatorRequest | 1 | 3 |
| Input | 2 | 4 |
| Instr | 2.17 | 13 |
| InstrCache | 1.5 | 9 |
| InvalidRequestException | 1 | 1 |
| LockedOutput | 1 | 1 |
| Main | 1 | 1 |
| People | 3 | 18 |
| UserInterface | 1.62 | 13 |
| Project | AHF | AIF | CF | MHF | MIF | PF |
|---|---|---|---|---|---|---|
| project | 88.64% | 39.73% | 31.82% | 25.76% | 0.00% | 100.00% |
| Class | CBO | DIT | LCOM | NOC | RFC | WMC |
|---|---|---|---|---|---|---|
| Car | 5 | 2 | 2 | 0 | 28 | 32 |
| CarDoorWrongException | 1 | 3 | 0 | 0 | 2 | 1 |
| Controllor | 7 | 2 | 1 | 0 | 49 | 58 |
| ElevatorRequest | 2 | 1 | 2 | 0 | 3 | 3 |
| Input | 2 | 2 | 1 | 0 | 6 | 4 |
| Instr | 3 | 1 | 2 | 0 | 7 | 13 |
| InstrCache | 3 | 1 | 1 | 0 | 9 | 9 |
| InvalidRequestException | 2 | 3 | 0 | 0 | 2 | 1 |
| LockedOutput | 1 | 1 | 1 | 0 | 2 | 1 |
| Main | 4 | 1 | 1 | 0 | 12 | 1 |
| People | 4 | 1 | 1 | 0 | 16 | 18 |
| UserInterface | 6 | 1 | 1 | 0 | 25 | 13 |
这里可以看到,我Controller 和 Car 的 WMC(类的加权方法)过高了,这是因为我有大量的算法位于Controller内部,而Car的方法进行了拆分。这里的确写的不是很优美,可以考虑把调度算法进行一些独立。

这次我发现多个controller之间的调用较为错误,如果重写,我应该新建一个线程,负责总领分配,三个controller在完成后,统一发送信号给总领分配的线程。
关于SOLID
| 首字母 | 指代 | 概念 |
|---|---|---|
| S | 单一功能原则 | 认为对象应该仅具有一种单一功能的概念。 |
| O | 开闭原则 | 认为“软件体应该是对于扩展开放的,但是对于修改封闭的”的概念。 |
| L | 里氏替换原则 | 认为“程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的”的概念。参考契约式设计。 |
| I | 接口隔离原则 | 认为“多个特定客户端接口要好于一个宽泛用途的接口”的概念。 |
| D | 依赖反转原则 | 认为一个方法应该遵从“依赖于抽象而不是一个实例”的概念。 依赖注入是该原则的一种实现方式。 |
(表来自 中文维基百科 SOLID)
其中,我没有达到的D,即我的Controller是依赖于实例的。
O原则较好的达到了——基本三次作业没有大改框架。
BUG与互测
在强测与互测中,我没有找到bug。但是在课下的个人测试中,我找到了一些自己的bug。
其中,尤为突出的bug是终止条件的判断。连续三次作业我都出现了问题。尤其是,假设在所有输出终止后才输入D,或者D紧跟最后一个输入,这两种情况如果不考虑完全,就容易出bug。
在互测的时候,我也发现有同学有^D如果输入较晚,则无法停止的问题,我觉得这是一个比较普遍的bug。
同时,这次作业由于需要准确的输入时间,我也写了一个自动输入脚本,我觉得自动化的测试是必要的。
心得体会
线程安全
保证线程安全要全盘的考虑,同时不能写的面向过程,要做到自己写的线程真正的安全,而不是仅仅是这种调用情况下的安全。
另外,关于死锁,要仔细分析锁的运用,不能无脑锁。
设计原则
面向对象需要的是一种优美的想法,或者,说的极端点,是一种强迫症——自己的代码应该很优美,很完美。
我在第二单元的学习中,渐渐感受到了设计的好的重要性,明白了一个好的设计既要有优美的结构,也可以有优秀的性能。当然,可能目前我对面向对象和java的理解还不够深入,但我相信在不断的OO课程的学习和练习中,对这方面的理解会越来越深入。
感谢为这门课程付出的老师和助教们。

浙公网安备 33010602011771号