Change and decay in all around I see…

— H. F. Lyte, Abide With Me

核心概念#

隨著程式演進,我們需要重新思考先前的決策並重做部分程式碼。這完全是自然的——程式碼需要演化,它不是靜態的東西。

不幸的是,軟體開發最常見的比喻是建築施工。但軟體更像是園藝——它是有機的,不是混凝土的。你在花園裡種植許多東西,有些茁壯、有些成了堆肥。你需要修剪過度生長的植物、拔除雜草、不斷監控花園的健康狀態並做出調整。

重寫、重做、重新架構程式碼統稱為重構(restructuring)。Martin Fowler 在 Refactoring 一書中將其定義為:

一種有紀律的技術,用於重組現有程式碼主體,改變其內部結構而不改變其外部行為。

關鍵要點:

  1. 這項活動是有紀律的,不是隨意亂改
  2. 外部行為不改變——這不是添加功能的時候

重構不應該是一次性的大規模活動——它是日常活動,採取低風險的小步驟,更像是除草和耙地,而不是把整座花園翻掉重來。

何時該重構?#

你在學到新東西、比去年(昨天、甚至十分鐘前)更理解某些事物時重構。以下情況可能觸發重構:

觸發時機英文術語說明
重複Duplication發現違反 DRY 原則的地方
非正交設計Nonorthogonal design發現可以更正交化的地方
過時的知識Outdated knowledge需求漂移、你對問題的理解加深
實際使用Usage某些功能比原先預想的更重要
效能Performance需要將功能搬到另一區域以改進效能
測試通過了The Tests Pass加了一小段程式碼、多通過一個測試,這是潛入整理你剛寫的東西的好時機

Tip 65 - Refactor Early, Refactor Often(及早重構,經常重構)

現實世界的顧慮#

時間壓力常被用來當作不重構的藉口,但這個藉口站不住腳:現在不重構,以後要修問題時依賴更多、更難改,需要更多時間。

用醫學比喻向他人解釋:把需要重構的程式碼想成「一個腫瘤」。你可以趁它還小時切除,或者等它長大擴散——到時切除會更昂貴也更危險。等得更久,你可能失去整個病人。

程式碼中的附帶損害隨時間累積也同樣致命(參見 Topic 3,軟體的熵)。

如何重構?#

Martin Fowler 提供了以下簡單建議:

  1. 不要同時重構和添加功能
  2. 在開始重構前確保有好的測試——盡可能頻繁地運行測試,這樣你會很快知道是否破壞了什麼
  3. 採取短小、刻意的步驟——移動欄位、拆分方法、重新命名變數。保持步驟小、每步之後測試,避免冗長的除錯

自動重構工具現已在大多數 IDE 和主流語言中可用。這些 IDE 可以重新命名變數和方法、自動傳播所需的變更。

如果需要超越重構、改變外部行為或介面,可以故意讓建構失敗——這樣舊的客戶端程式碼會編譯不過,你就知道哪裡需要更新。

下次看到一段不太對的程式碼時,修好它。管理痛苦:如果現在痛,但以後會更痛,不如現在就把它解決。記住 Topic 3 的教訓:不要容忍破窗。

相關章節#

  • Topic 3,軟體的熵
  • Topic 9,DRY——邪惡的重複
  • Topic 12,曳光彈
  • Topic 27,不要跑在車燈前面
  • Topic 44,命名
  • Topic 48,敏捷的本質