程式碼重複 vs. 錯誤抽象:在 DRY 與過度設計之間取得平衡

錯誤抽象的代價

維護重複的程式碼往往遠比管理錯誤的抽象來得便宜。雖然 DRY(Don't Repeat Yourself) 原則被廣泛教導,但過早或教條式地套用它,可能會導致「過度設計」的程式碼庫,這類程式碼庫比起僅有簡單重複的程式碼更難修改。

相較於被複雜、僵硬且與問題領域不相符的抽象所束縛的程式碼庫,欠缺設計的程式碼庫通常更易於操作。當抽象不正確時,開發者常常會與自己建立的框架搏鬥,導致系統脆弱,單一變更就必須在繼承類別或參數化函式的迷宮中穿梭。

區分偶然與真實的重複

並非所有程式碼重複都等同。是否抽象取決於重複是「真實」的還是「偶然」的。

  • 真實重複:相同的邏輯在多處被使用,因為它代表單一、普遍的真理(例如特定的物理公式)。這種情況應抽象,以確保「唯一真實來源」。
  • 偶然重複:兩個不同功能恰好在當前看起來相同,但未來會各自演化。強行將它們合併成單一抽象會產生「遠距耦合」,導致對其中一個功能的變更意外破壞另一個功能。

正如一位貢獻者所說,如果重複的程式碼能防止在將兩條分歧路徑強行合併時產生的錯誤,則需要重構。然而,若抽象僅為了便利而開始變得不便利,則它已失敗。

判斷何時抽象的策略

在重複與抽象之間取得平衡是核心工程技能。以下幾個啟發式方法可協助決定何時進行重構:

三次法則

許多開發者遵循「三次法則」,認為重複出現兩次是可以接受的,但當同樣的模式出現第三次時,就該抽象。此法則可避免因單一相似案例而過早抽象。

壓縮 vs. 抽象

減少程式碼行數(壓縮)提升概念上連貫的部份(抽象) 區分開來是有幫助的。高品質的工程更重視概念抽象,而非僅僅追求行數的壓縮。

介面優於繼承

為避免深層繼承樹——過度遵循 DRY 的常見結果——許多工程師偏好使用 介面。介面能提供正交的程式碼,使其保持彈性且較易維護,勝過僵硬的類別階層。

重複的反面觀點與風險

雖然「重複較便宜」的說法很受歡迎,但在大規模時仍有風險:

  • 規模下的維護負擔:對於某些專案,一旦規模達到一定程度(例如數十個客戶),重複的程式碼會變得極其昂貴。跨多個實例維護相同程式碼會消耗大量開發資源。
  • LLM 因素:在 AI 生成程式碼的時代,重複可能變得危險。大型語言模型(LLM)未必能在每個重複的模式上保持一致的修改,可能因不一致而引入錯誤。
  • 沉沒成本與慣性:有人認為廣泛的重複會產生一種維護成本,使得沒有人有動力去重構,形成與錯誤抽象不同的技術債。

工程取捨摘要

方法 主要好處 主要風險
接受重複 避免過早、僵硬的抽象;快速迭代更容易。 可能產生分歧;對重複性變更需要大量手動工作。
套用抽象 唯一真實來源;減少行數;全域更新更容易。 可能抽象錯誤;產生複雜耦合;撤銷困難

摘要:維護重複的程式碼通常比管理錯誤的抽象成本更低,但工程師必須在分歧風險與規模成本之間取得平衡。

標題:程式碼重複 vs. 錯誤抽象:在 DRY 與過度設計之間取得平衡

Sources