電梯調度題目集1~3 — 階段性總結與覆盤
本文為三次電梯調度題目集(單類實現 → SRP分層實現 → 引入乘客實體)的階段性技術博客。內容包含:前言、設計與分析(重點分析三次“單部電梯調度”題目的提交源碼)、採坑心得(包含數據、類設計結構、流程圖與測試結果)、改進建議與階段性總結。
一、前言
本階段包括三次題目集:
題目集1(單類實現):在一個 Elevator/Main 單類或扁平結構中完成電梯的全部功能:狀態管理、內外請求管理、調度邏輯和 I/O。該實現側重快速交付與功能完整性;題量為單個集中任務,涵蓋輸入解析、隊列管理與一步步運行模擬。難度定位為中等(對初學者而言理解並保證邊界情況正確處理具有挑戰)。
題目集2(SRP 重構):按照單一職責原則將系統拆分為若干類(電梯類、請求類/隊列類、控制類等),要求去除單類職責過多的問題,並增加對無效/重複請求的過濾。此階段強調設計質量與模塊接口規範,難度較題目集1提高,需理解設計模式與面向對象設計原則。
題目集3(引入 Passenger 實體):進一步演化外部請求模型,由<樓層,方向>變為<源樓層,目的樓層>,引入 Passenger 實體並在外部請求處理完成後將乘客目的地加入電梯內部隊列。此階段更貼近真實場景,設計和測試複雜度進一步上升。
三次題目集中共同目標是:實現功能正確、設計合理、邊界安全、可測試並可迭代改進。總體題量集中(每次為同類仿真程序),難度呈階梯上升:第1次偏實現,第2次偏設計,第3次偏建模與系統交互。
二、設計與分析
2.1 題目集1(單類實現)——實現快照與關鍵行為
概述
設計一個電梯類,具體包含電梯的最大樓層數、最小樓層數(默認為1層)當前樓層、運行方向、運行狀態,以及電梯內部乘客的請求隊列和電梯外部樓層乘客的請求隊列,其中,電梯外部請求隊列需要區分上行和下行。
電梯運行規則如下:電梯默認停留在1層,狀態為靜止,當有乘客對電梯發起請求時(各樓層電梯外部乘客按下上行或者下行按鈕或者電梯內部乘客按下想要到達的樓層數字按鈕),電梯開始移動,當電梯向某個方向移動時,優先處理同方向的請求,當同方向的請求均被處理完畢然後再處理相反方向的請求。電梯運行過程中的狀態包括停止、移動中、開門、關門等狀態。當電梯停止時,如果有新的請求,就根據請求的方向或位置決定移動方向。電梯在運行到某一樓層時,檢查當前是否有請求(訪問電梯內請求隊列和電梯外請求隊列),然後據此決定移動方向。每次移動一個樓層,檢查是否有需要停靠的請求,如果有,則開門,處理該樓層的請求,然後關門繼續移動。
使用鍵盤模擬輸入乘客的請求,此時要注意處理無效請求情況,例如無效樓層請求,比如超過大樓的最高或最低樓層。還需要考慮電梯的空閒狀態,當沒有請求時,電梯停留在當前樓層。
請編寫一個Java程序,設計一個電梯類,包含狀態管理、請求隊列管理以及調度算法,並使用一些測試用例,模擬不同的請求順序,觀察電梯的行為是否符合預期,比如是否優先處理同方向的請求,是否在移動過程中處理順路的請求等。為了降低編程難度,不考慮同時有多個乘客請求同時發生的情況,即採用串行處理乘客的請求方式(電梯只按照規則響應請求隊列中當前的乘客請求,響應結束後再響應下一個請求),具體運行規則詳見輸入輸出樣例。
輸入格式:
第一行輸入最小電梯樓層數。
第二行輸入最大電梯樓層數。
從第三行開始每行輸入代表一個乘客請求。電梯內乘客請求格式:<樓層數>
電梯外乘客請求格式:<乘客所在樓層數,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必須大寫)。
當輸入“end”時代表輸入結束(end不區分大小寫)。
輸出格式:
模擬電梯的運行過程,輸出方式如下:運行到某一樓層(不需要停留開門),輸出一行文本:
Current Floor: 樓層數 Direction: 方向
運行到某一樓層(需要停留開門)輸出兩行文本:
Open Door # Floor 樓層數
Close Door
輸入樣例:
在這裏給出一組輸入。例如:1
20
❤️,UP>
<5>
<6,DOWN>
<7>
<3>
end
輸出樣例:
在這裏給出相應的輸出。例如:Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
電梯運行過程詳解.pdf
代碼長度限制
16 KB
時間限制
400 ms
內存限制
64 MB
棧限制
8192 KB
題目集1 的提交以 Main + 內部靜態 Elevator 類形式完成。實現特點:輸入解析與調度邏輯耦合在 Main;Elevator 持有兩個隊列(內部請求 inside 與外部請求 outside),outside 為區分方向的請求隊列(OutsideRequest 內含 floor 與 dir)。調度規則按題目要求:電梯移動時優先處理同向請求,達到樓層檢查是否停靠並開關門;空閒時選擇最近請求決定方向。
主要類/模塊(摘要)
Main:負責 I/O、parse、將請求構造成 Request 並交給電梯。
Elevator:持有 current、dir、inside: Queue、outside: Queue,核心方法包括 addInside(...)、addOutside(...)、run()、getTarget()、needStop()、openDoor()。
設計優點
快速實現完整功能,便於測試與觀察運行軌跡;
邏輯直觀:getTarget() 在 dir==STOP 時用距離選擇目標,在移動狀態下尋找同向最近請求。
設計缺陷和可改進點(代碼級)
- 職責耦合:Main 同時承擔輸入解析和調度啓動,Elevator 內部仍承擔太多決策(目標選擇、隊列更新、打印輸出),不利於單元測試。
- 隊列語義不明確:inside、outside 均使用 Queue(FIFO),但電梯調度常需要按樓層排序或按方向篩選,不適合簡單隊列的語義;更合適的是 TreeSet/優先隊列或雙向鏈表並按方向維護順序。
- 重複/非法請求處理不足:實現中對無效樓層有檢查但沒有對連續重複請求做統一過濾,且 outside 隊列可能出現與當前方向不匹配但位置相同的請求被延遲處理。
- 開關門打印格式小瑕疵:openDoor() 中 System.out.print("Close Door") 可能與題目示例要求換行不一致。
類圖
Main:負責 I/O 與解析(main / parseRequest),包含靜態枚舉 RequestType 與 Direction(在源碼中為內部枚舉)。
Request:簡單數據載體(type、floor、direction),由 Main.parseRequest 構建並傳給 Elevator。
Elevator:承擔大量職責(當前層、方向、內部/外部隊列、調度算法、輸出)。圖中 OutsideRequest 為 Elevator 的內部類,聚合為 composition(*-- 表示強包含)。
該實現單體化,Elevator 方法(run、getTarget)包含複雜調度邏輯,解釋了為何該版本單方法複雜度高。
源碼複雜度與質量
代碼行數集中,Elevator.run() 方法較長,包含多個分支與循環,建議拆分為更小的私有方法以降低認知複雜度。
getTarget() 的距離計算邏輯在 dir==STOP 與移動狀態下兩套分支,有重複代碼片段可合併。
2.2 題目集2(SRP 重構)——模塊化與請求過濾
概述
對之前電梯調度程序進行迭代性設計,目的為解決電梯類職責過多的問題,類設計要求遵循單一職責原則(SRP),要求必須包含但不限於設計電梯類、乘客請求類、隊列類以及控制類,具體設計可參考如下類圖。
電梯運行規則與前階段單類設計相同,但要處理如下情況:
乘客請求樓層數有誤,具體為高於最高樓層數或低於最低樓層數,處理方法:程序自動忽略此類輸入,繼續執行
乘客請求不合理,具體為輸入時出現連續的相同請求,例如<3><3><3>或者<5,DOWN><5,DOWN>,處理方法:程序自動忽略相同的多餘輸入,繼續執行,例如<3><3><3>過濾為<3>
注意:本次作業類設計必須符合如上要求(包含但不限於乘客請求類、電梯類、請求隊列類及控制類,其中控制類專門負責電梯調度過程),凡是不符合類設計要求此題不得分,另外,PTA得分代碼界定為第一次提交的最高分代碼(因此千萬不要把第一次電梯程序提交到本次題目中測試)。
輸入格式:
第一行輸入最小電梯樓層數。
第二行輸入最大電梯樓層數。
從第三行開始每行輸入代表一個乘客請求。
電梯內乘客請求格式:<樓層數>
電梯外乘客請求格式:<乘客所在樓層數,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必須大寫)。
當輸入“end”時代表輸入結束(end不區分大小寫)。
輸出格式:
模擬電梯的運行過程,輸出方式如下:
運行到某一樓層(不需要停留開門),輸出一行文本:
Current Floor: 樓層數 Direction: 方向
運行到某一樓層(需要停留開門)輸出兩行文本:
Open Door # Floor 樓層數
Close Door
輸入樣例1:
在這裏給出一組輸入。例如:
1
20
❤️,UP>
<5>
<6,DOWN>
<7>
<3>
end
輸出樣例1:
在這裏給出相應的輸出。例如:
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
輸入樣例2:
在這裏給出一組輸入。例如:
1
20
❤️,UP>
❤️,UP>
<5>
<5>
<5>
<6,DOWN>
<7>
<7>
<3>
<22,DOWN>
<5,DOWN>
<30>
END
輸出樣例2:
在這裏給出相應的輸出。例如:
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Open Door # Floor 5
Close Door
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
代碼長度限制
16 KB
時間限制
400 ms
內存限制
64 MB
棧限制
8192 KB
題目集2 將系統拆分為:ElevatorCar(電梯實體)、RequestManager(請求註冊與隊列管理)、CallRequest/DestinationRequest(請求類)以及 ElevatorSystem(控制類)。此實現主動處理重複請求與越界請求,並將請求錄入 RequestManager 後由 ElevatorSystem 負責調度。
主要類/職責
ElevatorCar:封裝樓層位置、方向、運動方法(ascend()、descend())與門操作;只負責電梯內部狀態與基本動作。
RequestManager:負責註冊、去重(通過 lastCall/lastDestination 記錄)與隊列維護,提供 peek/poll 接口。
ElevatorSystem:集中控制調度、停靠判定、方向調整與移動行為。
設計優點
- 單一職責劃分明顯:每個類職責清晰,更便於單元測試與單元替換(如換隊列實現);
- 輸入預處理:RequestManager 做了越界與連續重複過濾(lastCall/lastDestination 邏輯),減少主調度負擔;
- 調度邏輯更明確:ElevatorSystem 根據 RequestManager 的 peek 信息決定方向,ElevatorCar 不再直接訪問請求隊列。
設計缺陷與潛在問題
- 去重策略欠靈活:使用 lastCall/lastDestination 僅能去掉“連續重複”請求,但不能去掉隊列中任意位置的重複(例如:<3>、<5>、<3>)會被視為有效。若要求全局去重需在 RequestManager 中採用 Set 結合順序結構。
- clearProcessedRequests() 方法語義含糊:示例代碼中對 clearProcessedRequests() 的實現可能誤刪未對應的請求,需仔細審查。
- 測試覆蓋需補強:邊界測試(無請求、所有無效請求、方向切換時多個同層請求)需要更多的單元測試用例。
類圖
MoveDirection:替代了 v1 中的 Direction,並提供 parseDirection()(源碼中存在)。
CallRequest / DestinationRequest:分別表示外部呼叫與車內目的地請求(數據對象 + 驗證方法)。
RequestManager:負責請求隊列管理(入隊、去重、peek/poll),實現 SRP(把請求管理從控制器抽離)。
ElevatorCar:只負責電梯狀態與物理操作(升降、開關門、顯示狀態)。
ElevatorSystem:控制器,負責調度決策、移動控制與停靠處理(核心調度邏輯在此)。該設計把複雜邏輯在類間拆分,降低了單方法複雜度(但仍有局部複雜方法)。
2.3 題目集3(引入 Passenger)——模型化與隊列聯動
概述
對之前電梯調度程序再次進行迭代性設計,加入乘客類(Passenger),取消乘客請求類,類設計要求遵循單一職責原則(SRP),要求必須包含但不限於設計電梯類、乘客類、隊列類以及控制類,具體設計可參考如下類圖。
電梯運行規則與前階段相同,但有如下變動情況:
乘客請求輸入變動情況:外部請求由之前的<請求樓層數,請求方向>修改為<請求源樓層,請求目的樓層>
對於外部請求,當電梯處理該請求之後(該請求出隊),要將<請求源樓層,請求目的樓層>中的請求目的樓層加入到請求內部隊列(加到隊尾)
注意:本次作業類設計必須符合如上要求(包含但不限於設計電梯類、乘客類、隊列類以及控制類),凡是不符合類設計要求此題不得分,另外,PTA得分代碼界定為第一次提交的最高分代碼(因此千萬不要把第一次及第二次電梯程序提交到本次題目中測試)。
輸入格式:
第一行輸入最小電梯樓層數。
第二行輸入最大電梯樓層數。
從第三行開始每行輸入代表一個乘客請求。
電梯內乘客請求格式:<樓層數>
電梯外乘客請求格式:<請求源樓層,請求目的樓層>,其中,請求源樓層表示乘客發起請求所在的樓層,請求目的樓層表示乘客想要到達的樓層。
當輸入“end”時代表輸入結束(end不區分大小寫)。
輸出格式:
模擬電梯的運行過程,輸出方式如下:
運行到某一樓層(不需要停留開門),輸出一行文本:
Current Floor: 樓層數 Direction: 方向
運行到某一樓層(需要停留開門)輸出兩行文本:
Open Door # Floor 樓層數
Close Door
輸入樣例1:
在這裏給出一組輸入。例如:
1
20
<5,4>
<5>
<7>
end
輸出樣例1:
在這裏給出相應的輸出。例如:
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Open Door # Floor 5
Close Door
Current Floor: 4 Direction: DOWN
Open Door # Floor 4
Close Door
輸入樣例2:
在這裏給出一組輸入。例如:
1
20
<5,9>
<8>
<9,3>
<4>
<2>
end
輸出樣例2:
在這裏給出相應的輸出。例如:
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Current Floor: 8 Direction: UP
Open Door # Floor 8
Close Door
Current Floor: 9 Direction: UP
Open Door # Floor 9
Close Door
Current Floor: 8 Direction: DOWN
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Open Door # Floor 4
Close Door
Current Floor: 3 Direction: DOWN
Current Floor: 2 Direction: DOWN
Open Door # Floor 2
Close Door
Current Floor: 3 Direction: UP
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Current Floor: 8 Direction: UP
Current Floor: 9 Direction: UP
Open Door # Floor 9
Close Door
Current Floor: 8 Direction: DOWN
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
代碼長度限制
16 KB
時間限制
400 ms
內存限制
64 MB
棧限制
8192 KB
題目集3 將外部請求由 <樓層,方向> 改為 <源樓層,目的樓層>,引入 Passenger 實體。系統要求:當外部請求在源樓層被處理(乘客上電梯)後,把該乘客目的樓層追加到電梯內部隊列(尾部)。此改動使得外部請求攜帶完整意圖數據,同時增加了內部隊列的動態生成行為。
主要類/職責
Passenger:表示從 sourceFloor 到 destinationFloor 的請求,包含 getDirection() 與 isValid() 校驗方法。
RequestManager:維護 externalRequests: LinkedList。
ElevatorSystem 與 ElevatorCar:延續 SRP 實現,但需要在處理外部請求時向內部請求隊列追加目的地。
設計優點
語義更豐富:Passenger 藴含上下行意圖,使調度邏輯在停靠時能同時把目的地轉入內部隊列;
更接近真實流程:上車後乘客在車內發出目的地請求的動作被寫入系統,便於後續擴展(比如乘客計數、容量限制、優先級)。
設計挑戰
- 外部請求的排序語義:當多個 Passenger 在 externalRequests 中,如何在不同方向與樓層之間選擇最優次序(如同向同路的合併)需要更細化的策略;
- 內部隊列尾部加入策略:直接 offer(destination) 將目標加在隊尾是簡單可行的實現,但在複雜流量下可能導致“反覆走樓層”的低效問題;需要考慮使用按方向排序或優先級數據結構;
- 併發與串行化設計:題目採用串行請求模型,若後續支持併發請求則需進一步考慮線程安全與事件驅動調度。
類圖(簡化 PlantUML)
Passenger:域模型(sourceFloor、destinationFloor),帶驗證與方向計算方法 getDirection()。這是語義最豐富的模型,有利於後續擴展(如計費、優先級)。
RequestManager:維護 externalRequests: LinkedList,並提供將乘客目的地追加到內部隊列的方法(addInternalRequestFromPassenger)。
ElevatorSystem:調度器讀取 getAllExternalRequests() / getAllInternalRequests() 來決定是否在當前層停靠並在停靠時將 Passenger.destination 加入內部隊列。
需要注意:源碼中對集合的遍歷/修改要用 Iterator 安全移除;圖中顯示組合關係(RequestManager 包含多個 Passenger)。
指標 題目集1 題目集2 題目集3
總行數 238 593 628
類數量 4 9 9
枚舉數量 2 1 1
方法數量 11 30 33
最大方法圈複雜度 22 21 20
平均方法複雜度 7.27 3.37 4.1
最長方法行數 43 37 37
SourceeMonitor報表
三、採坑心得(代碼提交與測試過程中遇到的問題與數據説明)
本節基於三次迭代開發中常見問題做詳實記錄,目的是讓後續復現更少踩坑、提交更高質量代碼。內容包含:問題描述、復現步驟、定位方法、改進措施與測試數據或流程圖説明。
3.1 常見問題一:輸入解析與字符串格式容錯
現象:學生提交中普遍在輸入解析處出錯(例如 trim()/大小寫判斷、< > 包裹與逗號分割異常),導致程序在讀取到 end/END 時不能正常終止或在遇到空行/額外空格時報錯。
定位方法:在 Scanner.nextLine() 後立即 trim(),並統一用 equalsIgnoreCase("end") 或正則 ^\send\s$ 判斷;解析請求前先判斷字符串是否以 < 開始並以 > 結束。
改進:實現一個 InputParser 小類負責:去掉首尾空白、檢查包裹字符、統一大小寫、對逗號分割後 parts.length 做嚴格範圍校驗、捕獲 NumberFormatException 並返回 Optional.empty()。
示例測試:
輸入 " ❤️,UP> " → 能正確解析為外部請求。
輸入 "END"、"end"、" End " → 能正常結束。
3.2 常見問題二:重複請求與隊列去重策略
現象:使用 lastRequest 去重只能屏蔽連續重複,但無法屏蔽“後到重複”。提交中部分隊列出現了多次相同目的地,導致不必要的停靠。
定位方法:閲讀 RequestManager.register… 的去重實現,發現僅與 lastCall 比對。
改進:建議採用 LinkedHashSet(保持插入順序的同時去重)或在 RequestManager 內維護 Set 索引(例如 Set internalSet 與 Set<Pair<Integer,Direction>> externalSet)來實現全局去重;入隊時先檢查 set.contains()。
數據參考(偽示例):
原策略:隊列 3,5,3 → 觸發 3 次停靠。
改進後:隊列 3,5,3 → 變為 3,5(若要求同一輪次內去重)。
3.3 常見問題三:方向切換與“同向優先”邊界
現象:實現 getTarget() 時若只對隊頭元素做判斷,會忽略隊列中存在的“更優”同向請求,導致電梯繞路或反覆切換方向。
定位方法:通過單步調試 run() 循環,觀察 sameDir 集合的構建方式以及是否遍歷整個隊列或僅看隊首。
改進:在查找同向請求時,應遍歷全部請求集合(或使用基於方向的有序集合),找出所有在當前方向上的請求並選擇最近的目標(按絕對樓層差最小)。數據結構推薦:TreeSet(自然排序)配合方向判斷來選擇 higher() 或 lower()。
3.4 常見問題四:開關門輸出與題目格式不一致
現象:打印行尾或換行不一致導致輸出與樣例不符,從而被自動評測判為 WA。
定位方法:審查所有 System.out.print 與 println 的調用,確保 Open Door # Floor n 與後續 Close Door 各佔一行。
改進:統一使用 println,或構建 Printer 封裝輸出行為,減少格式差異錯誤。
3.5 常見問題五:隊列併發修改與迭代器安全(在本題串行模型下少見)
現象:在處理 externalRequests 與 internalRequests 時,代碼用 for-each 遍歷集合同時在內部 remove(),會引發 ConcurrentModificationException(在使用 Iterator/remove() 的實現中需注意)。
改進:使用 Iterator 並調用 iterator.remove() 或先收集待刪元素到臨時列表,遍歷完畢後統一 removeAll()。
3.6 測試用例與結果説明(建議)
為保證覆蓋,建議至少包含以下測試類別:
- 基礎路徑:若干正常請求,上行/下行混合。
- 重複/無效請求:連續重複、超出樓層範圍、源與目的相同(對於 Passenger)。
- 方向切換場景:電梯處理完同向請求後需轉向處理反向請求的連續場景。
- 中途插入請求:模擬在電梯運行過程中新增請求(串行模型可在隊列尾追加),以觀察系統響應是否符合“順路優先”原則。
四、改進建議
4.1 改進數據結構
內部請求:使用 TreeSet(配合正/負權值)替代簡單 Queue,便於按樓層進行快速查找與方向過濾。
外部請求:使用 Map<Direction, TreeSet> 或維持兩個 TreeSet(上行集合、下行集合),可以快速定位同向最近樓層併合並順路請求。
4.2 改進調度算法
基於方向的最近優先:當電梯向上運行時,從上行集合選擇最小的 higher(current);當沒有同向請求時再切換方向。
合併同樓層請求:對同一樓層的內外請求合併處理,避免重複停靠開門。
容量與優先級擴展:如果後續要支持多人併發或容量限制,可以給 Passenger 添加 timestamp 與 priority 字段,調度器可採用加權選擇。
4.3 改進測試與可測性
單元測試:為 RequestManager、ElevatorCar、ElevatorSystem 編寫單元測試;對 RequestManager 的去重與隊列一致性編寫邊界測試。
日誌替代打印:使用可配置的 Logger(或 Printer 接口)替代直接 System.out,在測試中可以捕獲輸出並斷言格式與順序。
引入示例驅動測試:把輸入樣例做成若干 .txt 文件與期望輸出文件,做迴歸測試,保證輸出格式穩定。
4.4 設計層面建議
明確接口契約:例如 RequestManager 提供的 peek/poll/contains 應保證併發安全與一致性;接口註釋要明確語義(是否會刪除、是否線程安全)。
拆分大型方法:run()、startOperation() 等方法應拆為 determineNextTarget()、moveOneFloor()、handleStop() 等小函數以降低圈複雜度並便於單元測試。
五、總結
5.1 學到了什麼
- 從實現到設計的跨越:第一階段側重實現細節,第二階段學習到面向對象的 SRP 原則及模塊劃分的價值;第三階段通過引入 Passenger 實體體會到領域建模的重要性。
- 數據結構決定算法效率:從 Queue 到 TreeSet 的演化,直接影響查找同向最近請求的效率與代碼複雜度。
- 測試與格式一致性的重要性:自動評測對輸出格式敏感,統一的輸出封裝與系統化測試可避免很多 WA 問題。
5.2 需要進一步學習與研究的方向
高級調度算法:考慮多部電梯聯動、負載均衡與等待時間優化。
併發與事件驅動模型:從串行請求演化到併發請求時,需要學習線程安全、事件隊列與消息驅動架構。
形式化驗證:對邊界條件運用斷言或模型檢測,確保複雜場景下的正確性。