本单元的三次做业中,我采用了类似的策略:采用输入线程与电梯线程经过线程安全的调度器进行交互的方式。这种方式基本属于生产者-消费者模式。在调度器的设计方面,我主要采用synchronized关键字结合wait和notify方法完成互斥访问和同步控制。编程
Elevator类有且仅有一个public方法:run方法,仅仅负责执行电梯的运行逻辑。数组
Main类有且仅有一个public方法:main方法。但main方法既负责建立线程,又负责输入的处理与结束,具备多重责任。能够采起建立新的输入类和输入线程的方法解决这个设计问题。安全
Dispatcher类具备多个public方法,但每一个方法都有惟一肯定的职责,经过下面章节中UML类图便可确认这一特性。Dispatcher类负责将请求放置于每一楼层,供Elevator取用,知足了SRP原则。多线程
FloorConverter类有两个public方法,负责将楼层号与数组下标相互转换,知足了SRP原则。架构
FloorSelector类有两个public方法,分别用来判断当前层是否能够停靠和选择乘客的目的楼层。这两个职责都须要电梯的停靠信息,并且逻辑联系较为紧密,所以能够置于同一类中完成。测试
本次做业中除继承Thread类外没有使用任何继承,几乎都是经过修改已有实现完成新增功能,违反了OCP原则。spa
本次做业中除继承Thread类外没有使用任何继承,所以该原则无从体现。线程
本次做业中没有使用任何接口,所以该原则无从体现。设计
本次做业中除继承Thread类外没有使用任何继承,且没有使用任何接口,所以该原则无从体现。3d
经过上面的分析能够看出,扩展功能几乎必定须要经过改写已有的实现来完成。但因为类的public方法职责都较为明确,这样的设计能够为功能的扩展带来必定的便利。
Type Name | Method Name | LOC | CC | PC |
---|---|---|---|---|
Dispatcher | Dispatcher | 10 | 2 | 0 |
Dispatcher | setFinished | 6 | 1 | 0 |
Dispatcher | addRequest | 13 | 2 | 1 |
Dispatcher | getRequests | 14 | 2 | 2 |
Dispatcher | getTask | 30 | 9 | 1 |
Elevator | Elevator | 11 | 2 | 1 |
Elevator | run | 39 | 10 | 0 |
Elevator | stopOnFloor | 20 | 2 | 1 |
Elevator | getOn | 7 | 2 | 1 |
Elevator | getOff | 7 | 2 | 0 |
Elevator | go | 10 | 1 | 0 |
Main | main | 23 | 3 | 1 |
Type Name | NOF | NOPF | NOM | NOPM | LOC | WMC | NC | DIT | LCOM | FANIN | FANOUT |
---|---|---|---|---|---|---|---|---|---|---|---|
Dispatcher | 5 | 1 | 5 | 5 | 80 | 16 | 0 | 0 | 0.0 | 2 | 0 |
Elevator | 6 | 0 | 6 | 2 | 102 | 19 | 0 | 0 | 0.0 | 1 | 1 |
Main | 0 | 0 | 1 | 1 | 25 | 3 | 0 | 0 | -1.0 | 0 | 2 |
本次做业构建了三个类。这些类的封装较好,对外暴露的方法较少,且都具备明确的职责。大部分方法具备明确的职责,也较为简洁。可是,Elevator类的run方法展开了电梯的一次运行逻辑,总体较为复杂;Dispatcher类的getTask方法也较为复杂,不便改动和维护。
Type Name | Method Name | LOC | CC | PC |
---|---|---|---|---|
Dispatcher | Dispatcher | 10 | 2 | 0 |
Dispatcher | setFinished | 6 | 1 | 0 |
Dispatcher | addRequest | 13 | 2 | 1 |
Dispatcher | getRequests | 23 | 6 | 3 |
Dispatcher | getTask | 35 | 9 | 1 |
Dispatcher | getUpperTask | 8 | 3 | 1 |
Dispatcher | getLowerTask | 8 | 3 | 1 |
Elevator | Elevator | 12 | 2 | 2 |
Elevator | run | 39 | 10 | 0 |
Elevator | stopOnFloor | 20 | 2 | 1 |
Elevator | getOn | 7 | 2 | 1 |
Elevator | getOff | 7 | 2 | 0 |
Elevator | go | 10 | 1 | 0 |
FloorConverter | indexToFloor | 7 | 2 | 1 |
FloorConverter | floorToIndex | 7 | 2 | 1 |
Main | main | 27 | 4 | 1 |
Type Name | NOF | NOPF | NOM | NOPM | LOC | WMC | NC | DIT | LCOM | FANIN | FANOUT |
---|---|---|---|---|---|---|---|---|---|---|---|
Dispatcher | 6 | 1 | 7 | 5 | 111 | 26 | 0 | 0 | 0.0 | 2 | 1 |
Elevator | 8 | 0 | 6 | 2 | 105 | 19 | 0 | 0 | 0.0 | 1 | 2 |
FloorConverter | 0 | 0 | 2 | 2 | 16 | 4 | 0 | 0 | -1.0 | 2 | 0 |
Main | 0 | 0 | 1 | 1 | 29 | 4 | 0 | 0 | -1.0 | 0 | 2 |
本次做业与上一次做业架构极为类似,只是多了FloorConverter类。所以优缺点与上次大致相同,在此再也不赘述。
Type Name | Method Name | LOC | CC | PC |
---|---|---|---|---|
Dispatcher | Dispatcher | 10 | 2 | 0 |
Dispatcher | setFinished | 8 | 2 | 0 |
Dispatcher | addRequest | 13 | 2 | 1 |
Dispatcher | elevatorAddRequest | 9 | 2 | 2 |
Dispatcher | getRequests | 12 | 2 | 4 |
Dispatcher | filterRequests | 19 | 4 | 5 |
Dispatcher | decreaseRequestCount | 6 | 2 | 0 |
Dispatcher | getTask | 35 | 9 | 2 |
Dispatcher | hasValidRequest | 10 | 3 | 4 |
Dispatcher | getUpperTask | 11 | 4 | 2 |
Dispatcher | getLowerTask | 11 | 4 | 2 |
Elevator | Elevator | 28 | 5 | 3 |
Elevator | run | 40 | 10 | 0 |
Elevator | changeDirection | 8 | 2 | 0 |
Elevator | stopOnFloor | 18 | 2 | 1 |
Elevator | getOn | 10 | 2 | 1 |
Elevator | getOff | 15 | 3 | 0 |
Elevator | go | 12 | 1 | 0 |
FloorConverter | indexToFloor | 7 | 2 | 1 |
FloorConverter | floorToIndex | 7 | 2 | 1 |
FloorSelector | isStoppable | 12 | 4 | 2 |
FloorSelector | selectFloor | 15 | 5 | 4 |
FloorSelector | selectFloorA | 6 | 2 | 1 |
FloorSelector | selectFloorB | 18 | 6 | 3 |
FloorSelector | selectFloorC | 24 | 8 | 3 |
Main | main | 33 | 6 | 1 |
Type Name | NOF | NOPF | NOM | NOPM | LOC | WMC | NC | DIT | LCOM | FANIN | FANOUT |
---|---|---|---|---|---|---|---|---|---|---|---|
Dispatcher | 6 | 1 | 11 | 7 | 152 | 36 | 0 | 0 | 0.2727272727272727 | 2 | 2 |
Elevator | 10 | 0 | 7 | 2 | 143 | 25 | 0 | 0 | 0.0 | 0 | 3 |
FloorConverter | 0 | 0 | 2 | 2 | 16 | 4 | 0 | 0 | -1.0 | 3 | 0 |
FloorSelector | 3 | 0 | 5 | 2 | 80 | 25 | 0 | 0 | 1.0 | 2 | 1 |
Main | 0 | 0 | 1 | 1 | 35 | 6 | 0 | 0 | -1.0 | 0 | 1 |
本次做业构建了五个类。这些类的封装较好,对外暴露的方法较少,且都具备明确的职责,类间的协做关系也较为明确。大部分方法具备明确的职责,也较为简洁。可是,与前两次做业同样,Elevator类的run方法仍然展开了电梯的一次运行逻辑,总体较为复杂;Dispatcher类的getTask方法也较为复杂,不便改动和维护。
因为三次做业的线程交互模式较为相似,所以统一绘制UML时序图以下。
本单元做业在公测和互测中未出现任何bug。
在第三次做业的开发过程当中,因为Dispatcher类的getTask方法和getRequests方法判断请求是否为空的标准不一致,个人电梯线程在一些状况下出现了轮询,致使在中测中出现了CTLE的现象。我在本地经过在JProfiler中观察线程状态及CPU时间,并在程序中打印log的方式,最终定位了bug的位置,并进行了修复。
本次做业同第一单元不一样,须要作到在线交互。所以,本次做业的测试要求更高。可是,因为摸鱼心切,我仍然采用了手动构造测试用例的方法。所以,本单元我未能发现他人的任何bug。
经过本单元的三次做业,我对Java多线程编程有了一个初步的认识,并了解了一些简单的互斥访问与同步控制的方法。在多线程编程中,经过线程安全的共享对象来完成线程间交互是十分清晰而简洁的方式。经过对象锁,可使该对象在同一同步块内只能被一个线程访问,且不会被打断。再结合wait和notifyAll方法,能够避免轮询,高效利用CPU资源。此外,在设计中遵循SOLID原则及一些其余重要的设计原则也是十分重要的,这些原则保证了程序结构的清晰性和良好的可扩展性。在本单元做业中,部分设计原则未能体现甚至有所违背,在从此的编程中会多加注意。