軟體系統會持續演進。需求會變更,團隊會擴大,期限也會移動。隨著時間推移,這種自然的演進經常導致嚴重的技術負債。程式碼庫會變成錯綜複雜的依賴關係網,使得維護困難,新增功能也充滿風險。理解並理清這種複雜性的最有效方法之一,是透過架構可視化,特別是使用套件圖。本指南詳細說明了一個完整的案例研究,展示如何使用套件圖來重構遺留程式碼,以恢復一個陷入困境的系統的清晰度與可維護性。
遺留程式碼不只是舊程式碼;它是難以修改而不引入錯誤的程式碼。挑戰不僅在於撰寫新功能,更在於理解現有的結構。透過可視化軟體組件的高階組織,工程師能看見整片森林,而不至於迷失在樹林之中。透過繪製套件、依賴關係與介面,團隊可以識別出耦合過高的熱點,並規劃策略性的重構工作。

理解套件圖 📐
套件圖是一種UML(統一塑模語言)的實體,用來顯示系統組件的組織方式。它將相關元素分組為套件,代表邏輯邊界。這些圖表對於理解應用程式的巨觀結構至關重要。
- 套件: 一個包含相關類別、介面或其他套件的命名空間。透過將功能分組,有助於管理複雜度。
- 依賴: 一種關係,表示一個套件需要另一個套件才能運作。在圖表中,通常以虛線箭頭表示。
- 耦合: 軟體模組之間相互依賴的程度。低耦合是重構的主要目標之一。
- 内聚性: 套件內元素彼此相關的程度。高內聚性表示責任範圍明確。
在處理遺留系統時,逆向工程往往是必要的。這表示需分析現有的程式碼,以建立反映當前狀態的套件圖。此「現狀」模型可作為任何重構計畫的基準。
案例研究背景:企業計費系統 💰
在本案例研究中,我們檢視一個虛構的中型企業應用程式,稱為「企業計費系統」。該系統最初於五年前建立,用於處理訂閱服務的每月發票。隨著時間推移,新增了支援多幣別、稅額計算與第三方整合的功能。
問題: 開發速度顯著放緩。即使是更新稅率等簡單變更,也需在多個檔案中進行修改。錯誤經常在無關的模組中出現。團隊無法在不對整個系統進行回歸測試的情況下,有信心地部署新功能。
目標: 目標是降低模組間的耦合,提升可測試性,並建立一個模組化架構,以支援未來的成長,而無需進行完全重寫。
第一階段:探索與清點 🔍
任何重構工作的第一步都是理解當前狀態。沒有地圖,導航是不可能的。在此階段,團隊專注於逆向工程程式碼庫,以建立基準的套件圖。
1.1 識別邊界
團隊首先列出所有現有的命名空間或模組。他們記錄了每一支檔案與資料夾,以了解實際的結構。此清點顯示,幾個不同的業務領域混雜在同一個資料夾中。
- 核心計費: 包含發票產生與定價的邏輯。
- 報表: 包含產生PDF與CSV匯出的邏輯。
- 整合: 包含連接外部支付網關的邏輯。
- 工具: 包含共用的輔助函數、日期解析器和字串格式化工具。
1.2 建立依賴關係圖
元件辨識完成後,團隊繪製了元件之間的互動關係。他們使用自動化工具追蹤匯入語句與方法呼叫。此資料經過人工核對,以確保準確性。
最終產生的「現狀」套件圖顯示出重大問題:
- 而報表套件直接實例化來自核心計費.
- 而工具工具套件內包含了特定於計費的邏輯,違反了關注點分離原則。
- 在整合與核心計費.
第二階段:耦合與內聚性分析 🧩
圖表完成後,團隊分析了系統的結構健康狀況。他們尋找高耦合與低內聚性的跡象,這正是技術負債的指標。
2.1 识别「上帝物件」
「上帝物件」是一種知道太多或做太多事情的類別或模組。在舊系統中,一個命名為管理員的中央類別負責處理使用者驗證、計費邏輯與報表產生。這違反了單一責任原則。
2.2 依賴問題
團隊建立了一張依賴矩陣,以視覺化資訊的流動。矩陣中過多深色單元格表示系統中所有元件彼此依賴。
| 套件 A | 套件 B | 依賴類型 | 影響 |
|---|---|---|---|
| 報告 | 核心計費 | 直接匯入 | 高風險:計費變更會導致報告失效。 |
| 實用工具 | 核心計費 | 直接匯入 | 中等風險:共享狀態問題。 |
| 整合 | 報告 | 間接匯入 | 低風險:但長期會造成緊密耦合。 |
分析確認了報告模組與核心計費模組過於緊密耦合。如果計費邏輯變更,報告團隊必須立即更新程式碼。此瓶頸拖慢了開發進度。
第三階段:規劃目標狀態 🗺️
重構需要明確的目標。團隊定義了「未來應有」的架構。目標是分離關注點,使一個區域的變更不會波及到其他區域。
3.1 定義介面
介面在套件之間扮演合約的角色。透過定義明確的介面,套件可以在不了解彼此內部實作細節的情況下進行互動。團隊識別出關鍵的互動點:
- 計費服務: 提供計算金額和建立發票的方法。
- 發票儲存庫: 處理發票的資料持久化。
- 通知服務: 處理電子郵件和警示的發送。
3.2 重新繪製圖表
利用識別出的介面,團隊繪製了新的套件圖表。主要變更包括:
- 解耦報告: 報告套件將不再匯入核心計費類別。相反地,它將透過唯讀 DTO(資料傳輸物件)介面來取得資料。
- 集中化工具: 特定於計費的工具函式已移至核心計費套件中。僅剩通用工具保留在全域工具套件中。
- 打破循環依賴: 整合套件已重構,改為依賴通用付款介面,而非特定的計費實作。
第四階段:執行策略 🛠️
重構遺留程式碼具有風險。團隊採取謹慎且迭代的方式,以最小化破壞生產功能的機率。
4.1 緊勒樹模式
團隊採用一種模式,即在新架構中建立新功能,同時逐漸遷移舊功能。這使得系統始終保持可用狀態。
- 步驟 1: 在目標套件中建立新的介面。
- 步驟 2: 在目標套件中實作新邏輯。
- 步驟 3: 將流量從舊程式碼導向新程式碼。
- 步驟 4: 當覆蓋率足夠後,刪除舊程式碼。
4.2 漸進式重構
團隊將工作分解為小型且可驗證的任務。他們一次專注於一個套件。例如,他們從「工具」套件開始,因為這是風險最低的。
採取的行動:
- 從工具套件中提取日期格式化邏輯,並移入核心計費套件。
- 為資料檢索建立新的介面。
- 更新報表套件以使用新介面。
- 撰寫單元測試以驗證新介面的行為。
第五階段:驗證與維護 ✅
結構變更實施後,驗證至關重要。團隊確保系統行為與過去完全相同,但內部結構已得到改善。
5.1 回歸測試
執行自動化測試套件,以確保沒有功能遺失。團隊特別關注過去曾導致錯誤的邊界情況。
5.2 持續監控
即使在重構之後,系統仍必須持續監控。團隊制定了未來開發的指導原則,以防止相同反模式再次出現。
- 依賴規則:新程式碼必須遵循目標套件圖中定義的依賴方向。
- 程式碼審查:架構師審查合併請求,以確保套件邊界得到遵守。
- 文件:當架構發生重大變更時,套件圖會隨之更新。
關鍵經驗教訓 📚
本案例研究突顯了執行類似重構計畫的團隊應汲取的幾個關鍵教訓。
1. 可視化至關重要
你無法修復你看不見的問題。套件圖提供了理解問題範圍所需的可見性。若無此圖,團隊將只能猜測依賴關係。
2. 介面推動解耦
定義明確的介面,使團隊能夠獨立工作。報告團隊在介面定義完成後即可繼續工作,無需等待計費團隊完成內部邏輯。
3. 漸進式變更勝出
試圖一次重構所有內容,無異於失敗的配方。小規模且經過驗證的步驟能建立信心並降低風險。『絞殺榕』模式讓團隊能安全地遷移功能。
4. 維護是持續性的
重構不是一次性的事件,而是一種紀律。團隊必須承諾持續更新圖表並執行規則,以防止系統再次退化。
應避免的常見陷阱 ⚠️
即使有良好的計畫,團隊在執行階段仍經常出錯。以下是一些應留意的常見錯誤。
- 過度設計:建立過多抽象層會拖慢開發進度。應保持介面簡單,專注於當前需求。
- 忽略測試:重構時絕不能沒有安全網。若尚未有單元測試,請先撰寫。它們是你的安全網。
- 忽略業務:重構應支援業務目標。若重構未能提升速度或穩定性,可能不值得投入精力。
- 過時的圖表:過時的套件圖比沒有圖表更糟糕。它會帶來錯誤的安全感。應讓圖表與程式碼保持同步。
成功指標 📊
你如何知道重構是成功的?以下指標可協助衡量改善程度。
| 指標 | 重構前 | 重構後 |
|---|---|---|
| 耦合指數 | 高(許多相依性) | 低(較少相依性) |
| 環路複雜度 | 單一檔案中的複雜邏輯 | 模組間簡化的邏輯 |
| 建構時間 | 慢(完整重新編譯) | 更快(增量建構) |
| 缺點率 | 高 | 降低 |
長期追蹤這些指標,有助於向利益相關者展現架構工作的價值。
永續架構的最終考量 🏗️
重構遺留程式碼是一場馬拉松,而不是短跑。這需要耐心、紀律與清晰的遠見。透過使用套件圖來視覺化系統,團隊能更明智地決定應將努力投入於何處。
建立圖表的過程,往往比圖表本身更具價值。繪製相依性的行為迫使團隊深入理解系統。這種共通的理解,是健康程式碼庫的基礎。
請記住,架構不僅僅是關於結構;它更是關於溝通。套件圖能向新成員傳達設計意圖,降低上手與貢獻專案所需的認知負荷。
當你開始自己的重構之旅時,請持續關注逐步改善。不要期望第一次就達到完美。目標應是進步。每一次耦合的微小降低都是一次勝利。每一次新增的介面,都是朝向更易維護系統的一步。
透過遵循這些原則,並使用套件圖作為分析與規劃的工具,你可以將混亂的遺留系統轉化為穩健且模組化的架構。這種方法確保軟體能隨著其所服務的商業需求一同演進。











