本單元的三次做業中,我採用了類似的策略:採用輸入線程與電梯線程經過線程安全的調度器進行交互的方式。這種方式基本屬於生產者-消費者模式。在調度器的設計方面,我主要採用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原則及一些其餘重要的設計原則也是十分重要的,這些原則保證了程序結構的清晰性和良好的可擴展性。在本單元做業中,部分設計原則未能體現甚至有所違背,在從此的編程中會多加註意。