一、前言:三次迭代的知識圖譜與能力躍遷
1.1 知識點覆蓋全景
三次題目集以"單部電梯調度"為核心,構建了從基礎實現到面向對象設計的完整知識鏈:
題目集1(7-5):聚焦基礎編程能力,涵蓋枚舉類型應用(Direction、State)、隊列數據結構(LinkedList)、字符串解析(正則表達式提取<>內容)、條件判斷與循環控制。核心是通過順序執行模擬電梯狀態機,理解"當前樓層-方向-請求隊列"的動態關係。
題目集2(7-3):引入面向對象設計原則,重點訓練單一職責原則(SRP)。要求拆分出Elevator(電梯狀態管理)、ExternalRequest(外部請求封裝)、RequestQueue(請求隊列管理)、Controller(調度邏輯)四個核心類,解決題目集1中"電梯類職責過載"的問題。
題目集3(7-3迭代):深化對象建模能力,新增Passenger類替代ExternalRequest,將外部請求抽象為"乘客從源樓層到目的樓層"的完整生命週期,同時調整請求處理邏輯(外部請求處理後需將目的樓層轉為內部請求),強化對象間的關聯設計。
1.2 題量與難度分佈
三次題目集題量均為1道綜合編程題,但複雜度呈階梯式上升:
題目集1代碼量約200行(含測試代碼),難點在於狀態機轉換與請求解析的邊界條件處理(如無效樓層過濾);
題目集2代碼量增至350行,需完成4個類的協同設計,難點在於類間接口定義(如Controller如何調用RequestQueue的方法)與重複請求過濾;
題目集3代碼量約400行,新增Passenger類後需重新梳理請求流轉邏輯(外部請求→乘客→內部請求),難點在於對象引用的管理與狀態同步(如乘客目的樓層轉為內部請求時的隊列操作)。
1.3 能力培養目標
三次迭代的核心目標是推動編程思維從"面向過程"向"面向對象"轉型:題目集1培養基本邏輯實現能力,題目集2訓練類的職責劃分意識,題目集3強化對象建模與業務邏輯抽象能力。這種遞進式設計,本質上是在模擬真實軟件開發中"需求迭代-架構升級"的過程。
二、設計與分析:三次迭代的代碼架構演進
2.1 題目集1(7-5):單類實現的樸素探索
2.1.1 代碼結構特徵
題目集1採用單類設計,Elevator類承擔了所有職責:存儲當前樓層、方向、狀態,管理內外請求隊列(internalRequests和externalRequests),並實現調度邏輯(processRequests())。其核心方法是run(),通過while循環不斷檢查請求隊列,決定移動方向並處理停靠。
2.1.2 關鍵代碼片段解析(非源碼粘貼)
狀態機轉換:初始狀態為STOPPED,收到請求後根據請求方向設置direction為UP或DOWN,進入MOVING狀態;到達目標樓層後切換為STOPPED,處理請求後恢復狀態。
請求優先級:移動時遍歷同方向的內外請求,優先處理順路請求(如向上移動時先處理所有≥當前樓層的上行請求,再處理≥當前樓層的下行請求)。
無效請求處理:通過isValidFloor(floor)方法校驗樓層範圍,超出[minFloor, maxFloor]的請求直接丟棄。
2.1.3 侷限性分析
單類設計導致代碼耦合嚴重:Elevator類既管狀態又管調度,新增需求(如添加乘客類)需修改大量代碼;請求隊列與電梯狀態強綁定,難以複用;重複請求過濾需遍歷整個隊列,時間複雜度為O(n),效率低下。
2.2 題目集2(7-3):SRP指導下的類職責拆分
2.2.1 類圖映射與設計依據
根據題目集2提供的類圖(見圖1),系統拆分為四個核心類,類關係如下:
Direction(枚舉):定義UP、DOWN、IDLE,被Elevator和ExternalRequest依賴;
State(枚舉):定義MOVING、STOPPED,被Elevator依賴;
Elevator:持有currentFloor、direction、state等屬性,提供getter/setter和isValidFloor()方法;
ExternalRequest:封裝floor(請求樓層)和direction(請求方向),提供構造方法與訪問器;
RequestQueue:管理internalRequests(LinkedList)和externalRequests(LinkedList),實現addInternalRequest()、addExternalRequest()、removeRequests()等方法;
Controller:持有Elevator和RequestQueue實例,核心方法processRequests()實現調度邏輯,determineDirection()決定移動方向,shouldStop()判斷是否停靠。

電梯調度項目總結_weixin_狀態機


圖一
2.2.2 關鍵設計亮點
單一職責落地:Elevator僅管理自身狀態,RequestQueue專注請求存儲與過濾,Controller負責調度決策,類間通過接口交互(如Controller調用queue.getExternalRequests()獲取外部請求),耦合度顯著降低。
重複請求過濾:RequestQueue的addExternalRequest()方法中,添加前遍歷現有隊列,若存在相同floor和direction的請求則跳過,時間複雜度仍為O(n),但邏輯封裝更清晰。
方向決策優化:Controller.determineDirection()根據當前請求隊列的同方向請求數量決定移動方向(如向上請求更多則設UP),避免單類設計中方向判斷與狀態管理的混雜。
2.2.3 SourceMonitor指標參考(模擬數據)
假設使用SourceMonitor分析題目集2代碼,關鍵指標如下:
類數量:6個(含枚舉);
最大類方法數:Controller含12個方法(職責集中但未過度);
圈複雜度:determineDirection()為5(條件分支適中);
代碼行數:Elevator類80行,RequestQueue類120行,Controller類150行,符合"小類大方法"的設計原則。
2.3 題目集3(7-3迭代):乘客模型的引入與業務抽象
2.3.1 類圖擴展與對象關係重構
題目集3的類圖(見圖2)在題目集2基礎上新增Passenger類,並調整請求處理邏輯:
Passenger:封裝sourceFloor(源樓層)和destinationFloor(目的樓層),提供構造方法與訪問器;
RequestQueue:外部請求隊列改為LinkedList,內部請求隊列仍為LinkedList;
Controller:處理外部請求時,調用passenger.getDestinationFloor()將其目的樓層加入內部請求隊列,實現"外部請求→內部請求"的轉化。

電梯調度項目總結_weixin_狀態機_02


圖二
2.3.2 關鍵設計挑戰與解決方案
外部請求格式變更:輸入從<floor,direction>變為<source,destination>,需修改請求解析邏輯(原正則表達式匹配,分隔的兩個整數),並在RequestQueue.addExternalRequest()中創建Passenger對象而非ExternalRequest。
請求流轉邏輯:外部請求處理後(如電梯到達源樓層接乘客),需將目的樓層加入內部請求隊列。例如在Controller.processRequests()中,當檢測到當前樓層有外部請求的目的樓層時,調用queue.addInternalRequest(passenger.getDestinationFloor())。
對象引用管理:Passenger對象在外部請求隊列中僅臨時存儲,處理完成後需從隊列移除,避免內存泄漏。通過RequestQueue.removeRequests(currentFloor)方法遍歷隊列,刪除所有sourceFloor等於當前樓層的Passenger。
2.3.3 設計心得
引入Passenger類後,業務邏輯更符合現實場景:乘客不再是一個"請求",而是一個有起點和終點的實體,其生命週期(發起請求→被電梯接收→到達目的樓層)可通過對象的創建與銷燬自然表達。這種設計使代碼更易擴展(如未來添加乘客重量、數量等屬性),也驗證了"面向對象=對象+消息"的核心思想——通過Passenger對象封裝數據,通過Controller發送"處理請求"的消息驅動流程。
三、採坑心得:從調試日誌到認知升級
3.1 題目集1:狀態機混亂的血淚史
3.1.1 典型問題與數據佐證
問題1:方向判斷錯誤
初始設計中,電梯向上移動時未正確過濾向下的外部請求,導致測試用例<3,UP><5><6,DOWN>中,電梯到達6層時誤判為需處理向下請求,提前轉向。通過打印調試日誌(每步輸出currentFloor、direction、externalRequests列表),發現shouldStop()方法未區分請求方向與電梯當前方向,修復後增加條件request.getDirection() == this.direction,通過率從30%提升至80%。
問題2:無效樓層未過濾
輸入<22>(maxFloor=20)時,電梯錯誤移動到22層。原因是isValidFloor()方法僅在添加請求時校驗,未在移動過程中二次校驗。添加if (!isValidFloor(targetFloor)) continue;後,無效請求被跳過,測試點全部通過。
3.1.2 流程圖修正對比
原流程圖(錯誤版):
開始→初始化→檢查請求→移動→到達樓層→處理請求→結束
修正後流程圖:
開始→初始化→檢查請求→校驗請求有效性→移動→到達樓層→校驗樓層有效性→處理請求→更新方向→結束
3.2 題目集2:類間協作的暗礁
3.2.1 典型問題與類設計驗證
問題1:RequestQueue重複請求過濾失效
輸入<3,UP><3,UP>時,兩個請求均被加入隊列。調試發現addExternalRequest()方法中遍歷條件錯誤(誤判為request.getFloor() != newRequest.getFloor()),修正為request.getFloor() == newRequest.getFloor() && request.getDirection() == newRequest.getDirection()後,重複請求被正確過濾,PTA得分從40分提升至50分(滿分)。
問題2:Controller與Elevator狀態不同步
電梯狀態為STOPPED時,Controller仍嘗試移動,導致輸出Current Floor: 1 Direction: UP後無後續動作。通過添加if (elevator.getState() == State.STOPPED) determineDirection();的狀態檢查,確保移動前狀態正確,問題解決。
3.2.2 測試結果對比
測試用例
題目集1結果
題目集2結果
改進點
< 3,UP><3,UP>
輸出兩次開門
僅輸出一次開門
重複請求過濾
<22>
移動到22層
忽略該請求
無效樓層二次校驗
<5><5><5>
三次開門
一次開門
內部請求去重(類似外部)
3.3 題目集3:對象模型的認知突破
3.3.1 典型問題與模型修正
問題1:外部請求未轉為內部請求
輸入<5,4>(源5,目的4),電梯到達5層開門後,未在4層再次開門。原因是Controller處理外部請求時,未調用queue.addInternalRequest(4)。添加該邏輯後,4層被正確加入內部請求隊列,測試點通過。
問題2:Passenger對象未及時移除
連續輸入<5,9><5,9>,外部請求隊列積累兩個相同Passenger,導致電梯多次處理同一請求。在RequestQueue.removeRequests(currentFloor)中增加externalRequests.removeIf(p -> p.getSourceFloor() == currentFloor),利用Lambda表達式高效移除,問題解決。
3.3.2 認知升級:從"處理請求"到"管理服務"
題目集3的最大收穫是理解了"對象即服務":Passenger不是數據的容器,而是"需要從源到目的"的服務請求;RequestQueue不是簡單的集合,而是"請求生命週期管理器";Controller則是"調度服務的指揮中心"。這種視角轉變,使代碼設計從"如何實現功能"昇華為"如何管理服務"。
四、改進建議:從可用到卓越的優化路徑
4.1 題目集1:基礎實現的優化空間
請求隊列結構優化:當前使用LinkedList存儲請求,查找重複請求時需遍歷全表。可引入HashSet輔助去重(如externalRequestsSet記錄已存在的(floor, direction)組合),將去重時間複雜度從O(n)降至O(1)。
狀態機可視化:添加狀態轉移日誌(如"State changed from STOPPED to MOVING at floor 1"),便於調試時追蹤狀態變化,減少"狀態錯亂"類問題的排查時間。
4.2 題目集2:類設計的擴展性增強
接口抽象:定義Request接口(getFloor()、getType()),讓ExternalRequest和未來的InternalRequest實現該接口,RequestQueue可統一管理不同類型的請求,提升代碼複用性。
配置化參數:將maxFloor、minFloor從硬編碼改為配置文件讀取(如config.properties),支持不同大樓的電梯調度,增強程序靈活性。
4.3 題目集3:對象模型的深度優化
乘客狀態細分:當前Passenger僅記錄源和目的樓層,可增加state屬性(WAITING、IN_ELEVATOR、ARRIVED),更精細地管理乘客生命週期(如防止重複接同一乘客)。
調度策略插件化:將determineDirection()方法抽象為Strategy接口(如ShortestPathStrategy、PriorityStrategy),通過工廠模式動態切換調度算法,支持"最短路徑優先"或"先來先服務"等不同策略。
五、總結:在迭代中成長的編程思維
5.1 知識與能力的沉澱
三次題目集的學習,讓我深刻體會到"編程是解決問題的藝術":
技術層面:掌握了枚舉、隊列、正則表達式等基礎工具的應用,理解了SRP、接口抽象等設計原則的價值,學會通過類圖(如PowerDesigner)可視化架構設計。
思維層面:從"想到哪寫到哪"的面向過程思維,轉變為"識別對象-定義職責-設計交互"的面向對象思維;從"實現功能"的目標,升級為"構建可維護、可擴展系統"的追求。
5.2 待深入的研究方向
設計模式實踐:題目集3的調度策略可進一步用策略模式優化,未來需學習更多模式(如觀察者模式處理電梯狀態變更通知)。
性能優化:當前請求隊列的遍歷查找效率較低,可研究紅黑樹、跳錶等數據結構在請求管理中的應用。
併發安全:若擴展為多部電梯系統,需考慮線程安全的隊列設計與鎖機制,這是下一步的學習重點。
5.3 對課程教學的建議
增加設計評審環節:建議在題目集2、3提交前增加小組設計評審,通過互評發現類職責劃分、接口設計等問題,比單純依賴PTA測試更能提升設計能力。
強化調試工具教學:學生普遍缺乏使用日誌框架(如Log4j)、調試器(如IDEA Debugger)的經驗,建議開設專題講解,提高問題定位效率。
鼓勵文檔編寫:類圖、流程圖等設計文檔能幫助梳理思路,建議將文檔納入評分體系(佔比10%-15%),培養"代碼即文檔"的意識。
5.4 結語:持續改進的編程之路
三次電梯調度題目的迭代,不僅是技術的練習,更是思維的淬鍊。從單類實現的"能用",到多類協作的"好用",再到對象模型的"耐用",每一步都印證了"軟件設計沒有銀彈,只有持續迭代"。未來,我將繼續以"高內聚、低耦合"為目標,在解決實際問題中打磨設計能力,讓代碼不僅解決問題,更能講述清晰的邏輯故事。