Change and decay in all around I see…
— H. F. Lyte, Abide With Me
核心概念#
隨著程式演進,我們需要重新思考先前的決策並重做部分程式碼。這完全是自然的——程式碼需要演化,它不是靜態的東西。
不幸的是,軟體開發最常見的比喻是建築施工。但軟體更像是園藝——它是有機的,不是混凝土的。你在花園裡種植許多東西,有些茁壯、有些成了堆肥。你需要修剪過度生長的植物、拔除雜草、不斷監控花園的健康狀態並做出調整。
重寫、重做、重新架構程式碼統稱為重構(restructuring)。Martin Fowler 在 Refactoring 一書中將其定義為:
一種有紀律的技術,用於重組現有程式碼主體,改變其內部結構而不改變其外部行為。
關鍵要點:
- 這項活動是有紀律的,不是隨意亂改
- 外部行為不改變——這不是添加功能的時候
重構不應該是一次性的大規模活動——它是日常活動,採取低風險的小步驟,更像是除草和耙地,而不是把整座花園翻掉重來。
何時該重構?#
你在學到新東西、比去年(昨天、甚至十分鐘前)更理解某些事物時重構。以下情況可能觸發重構:
| 觸發時機 | 英文術語 | 說明 |
|---|---|---|
| 重複 | Duplication | 發現違反 DRY 原則的地方 |
| 非正交設計 | Nonorthogonal design | 發現可以更正交化的地方 |
| 過時的知識 | Outdated knowledge | 需求漂移、你對問題的理解加深 |
| 實際使用 | Usage | 某些功能比原先預想的更重要 |
| 效能 | Performance | 需要將功能搬到另一區域以改進效能 |
| 測試通過了 | The Tests Pass | 加了一小段程式碼、多通過一個測試,這是潛入整理你剛寫的東西的好時機 |
Tip 65 - Refactor Early, Refactor Often(及早重構,經常重構)
現實世界的顧慮#
時間壓力常被用來當作不重構的藉口,但這個藉口站不住腳:現在不重構,以後要修問題時依賴更多、更難改,需要更多時間。
用醫學比喻向他人解釋:把需要重構的程式碼想成「一個腫瘤」。你可以趁它還小時切除,或者等它長大擴散——到時切除會更昂貴也更危險。等得更久,你可能失去整個病人。
程式碼中的附帶損害隨時間累積也同樣致命(參見 Topic 3,軟體的熵)。
如何重構?#
Martin Fowler 提供了以下簡單建議:
- 不要同時重構和添加功能
- 在開始重構前確保有好的測試——盡可能頻繁地運行測試,這樣你會很快知道是否破壞了什麼
- 採取短小、刻意的步驟——移動欄位、拆分方法、重新命名變數。保持步驟小、每步之後測試,避免冗長的除錯
自動重構工具現已在大多數 IDE 和主流語言中可用。這些 IDE 可以重新命名變數和方法、自動傳播所需的變更。
如果需要超越重構、改變外部行為或介面,可以故意讓建構失敗——這樣舊的客戶端程式碼會編譯不過,你就知道哪裡需要更新。
下次看到一段不太對的程式碼時,修好它。管理痛苦:如果現在痛,但以後會更痛,不如現在就把它解決。記住 Topic 3 的教訓:不要容忍破窗。
相關章節#
- Topic 3,軟體的熵
- Topic 9,DRY——邪惡的重複
- Topic 12,曳光彈
- Topic 27,不要跑在車燈前面
- Topic 44,命名
- Topic 48,敏捷的本質