程式碼重複 vs. 錯誤抽象:在 DRY 與過度設計之間取得平衡
錯誤抽象的代價
維護重複的程式碼往往遠比管理錯誤的抽象來得便宜。雖然 DRY(Don't Repeat Yourself) 原則被廣泛教導,但過早或教條式地套用它,可能會導致「過度設計」的程式碼庫,這類程式碼庫比起僅有簡單重複的程式碼更難修改。
相較於被複雜、僵硬且與問題領域不相符的抽象所束縛的程式碼庫,欠缺設計的程式碼庫通常更易於操作。當抽象不正確時,開發者常常會與自己建立的框架搏鬥,導致系統脆弱,單一變更就必須在繼承類別或參數化函式的迷宮中穿梭。
區分偶然與真實的重複
並非所有程式碼重複都等同。是否抽象取決於重複是「真實」的還是「偶然」的。
- 真實重複:相同的邏輯在多處被使用,因為它代表單一、普遍的真理(例如特定的物理公式)。這種情況應抽象,以確保「唯一真實來源」。
- 偶然重複:兩個不同功能恰好在當前看起來相同,但未來會各自演化。強行將它們合併成單一抽象會產生「遠距耦合」,導致對其中一個功能的變更意外破壞另一個功能。
正如一位貢獻者所說,如果重複的程式碼能防止在將兩條分歧路徑強行合併時產生的錯誤,則需要重構。然而,若抽象僅為了便利而開始變得不便利,則它已失敗。
判斷何時抽象的策略
在重複與抽象之間取得平衡是核心工程技能。以下幾個啟發式方法可協助決定何時進行重構:
三次法則
許多開發者遵循「三次法則」,認為重複出現兩次是可以接受的,但當同樣的模式出現第三次時,就該抽象。此法則可避免因單一相似案例而過早抽象。
壓縮 vs. 抽象
將 減少程式碼行數(壓縮) 與 提升概念上連貫的部份(抽象) 區分開來是有幫助的。高品質的工程更重視概念抽象,而非僅僅追求行數的壓縮。
介面優於繼承
為避免深層繼承樹——過度遵循 DRY 的常見結果——許多工程師偏好使用 介面。介面能提供正交的程式碼,使其保持彈性且較易維護,勝過僵硬的類別階層。
重複的反面觀點與風險
雖然「重複較便宜」的說法很受歡迎,但在大規模時仍有風險:
- 規模下的維護負擔:對於某些專案,一旦規模達到一定程度(例如數十個客戶),重複的程式碼會變得極其昂貴。跨多個實例維護相同程式碼會消耗大量開發資源。
- LLM 因素:在 AI 生成程式碼的時代,重複可能變得危險。大型語言模型(LLM)未必能在每個重複的模式上保持一致的修改,可能因不一致而引入錯誤。
- 沉沒成本與慣性:有人認為廣泛的重複會產生一種維護成本,使得沒有人有動力去重構,形成與錯誤抽象不同的技術債。
工程取捨摘要
| 方法 | 主要好處 | 主要風險 |
|---|---|---|
| 接受重複 | 避免過早、僵硬的抽象;快速迭代更容易。 | 可能產生分歧;對重複性變更需要大量手動工作。 |
| 套用抽象 | 唯一真實來源;減少行數;全域更新更容易。 | 可能抽象錯誤;產生複雜耦合;撤銷困難 |
摘要:維護重複的程式碼通常比管理錯誤的抽象成本更低,但工程師必須在分歧風險與規模成本之間取得平衡。
標題:程式碼重複 vs. 錯誤抽象:在 DRY 與過度設計之間取得平衡