核心觀點#

作者 Robert C. Martin 列出一系列具有「壞味道(Bad Smells)」的程式碼特徵。
當你嗅到這些味道時,通常代表該處隱藏著設計問題,值得我們停下來重構。

這份列表真正的貢獻,並非在於規則,而在於提出一個價值體系
軟體工藝(Software Craftsmanship)的專業性,正來自於對這些價值觀的堅持。


註解 (Comments)#

註解是雙面刃,壞的註解比沒有註解更糟。

代碼壞味道說明
C1包含不妥資訊註解應只保留技術性(程式設計或架構面)的內容。
像修改歷史、JIRA 單號等日誌資訊應交給 Git 處理
C2廢棄的註解過時、不相關或不正確的註解會誤導讀者,
應立即更新或刪除
C3多餘的註解如果程式碼本身已夠清楚(Self-documenting),
就不需要再寫註解重述一遍
C4拙劣的註解如果必須寫註解,請字斟句酌,確保文法正確且詞意精準,
別像在寫隨手便條

C5: 被註解掉的程式碼 (Commented-Out Code)
這是最常見的壞味道,請直接刪除,不要擔心遺失。
版本控制系統(Git)會幫你記得它。


開發環境 (Environment)#

開發環境的設置應當極簡化,以減少人為錯誤。

代碼壞味道說明
E1建立只需單一步驟系統的建置(Build)不應包含從倉庫簽出程式碼以外的複雜指令。
應能一鍵完成
E2測試只需單一步驟執行所有單元測試應該只需要一個按鈕或一個指令

函式 (Functions)#

函式設計的核心在於「專注」與「簡潔」。

代碼壞味道說明
F1參數過多參數量應由少至多排序:0 < 1 < 2 « 3。
超過三個通常代表設計有問題
F2輸出型參數讀者習慣將參數視為輸入,避免將參數用作輸出(回傳值)
F3旗標參數
(Flag Arguments)
傳入布林值通常意味著函式做了「兩件事」
(True 做一件事,False 做另一件事),違反單一職責原則
F4未被呼叫的函式也就是死函式(Dead Function),請勇敢刪除它

一般性問題 (General)#

這是最龐大的一類,涵蓋了設計原則與實作細節。

抽象與依賴#

代碼壞味道說明
G6錯誤的抽象層次高層次概念應放在基底類別,
低層次實作細節應放在衍生類別
G7基底相依於衍生類別基底類別不該知道關於衍生類別的任何資訊
G14依戀情節
(Feature Envy)
當一個類別方法對「另個類別」的變數與函式感興趣的程度,
超過對自己類別的興趣時,代表該方法可能放錯位置了
G22邏輯相依 vs 實體相依相依模組不該對被相依模組有任何
「預先假設(Assumption)」,應明確詢問所有必要資訊
G36避免傳遞性導覽
(Transitive Navigation)
也就是笛米特法則(Law of Demeter)。
避免寫出 a.getB().getC().doSomething() 這樣的程式碼,
模組不應了解它所合作物件的內部細節

實作紀律#

代碼壞味道說明
G5程式碼重複 (Duplication)這是萬惡之源。
重複代表著抽象化的缺失
G9死程式碼 (Dead Code)不會被執行的程式碼
(如永遠為 false 的 if 區塊)應被移除
G10垂直分隔變數應定義在靠近使用處;
私有函式應定義在呼叫它的函式正下方
G25用具名常數取代魔術數字讓數字擁有語意

意圖與表達#

代碼壞味道說明
G16意圖模糊程式碼應當簡潔且具表達力
G19變數應具解釋性將複雜的計算結果存入具有良好命名的中繼變數中,
能讓邏輯更清晰
G20函式名稱應「說到做到」名稱與行為必須一致,不能有誤導
G28封裝條件判斷將複雜的 if 判斷式封裝成一個
回傳布林值的函式(例如 if (shouldBeDeleted(timer))
優於 if (timer.hasExpired() && !timer.isRecurrent())
G29避免否定條件判斷正向思考較容易理解,
盡量避免 if (!isNotAvailable()) 這種雙重否定

Java 語言特性 (Java)#

針對 Java 語言的特定建議:

代碼壞味道說明
J1善用萬用字元引入若引入同一個套件的多個類別,
使用 import package.* 可以避免引入列表過長
(註:這點在不同團隊可能有不同規範)
J2不要繼承常數應使用 static import 來引入常數,
而非透過繼承介面來取得
J3善用 Enum盡量用列舉(Enum)取代 public static final int

命名 (Names)#

好的命名能省下閱讀文件的時間。

代碼壞味道說明
N1名稱具描述性具描述性的名稱能大幅提升可讀性
N2依據抽象層次命名不要讓實作細節洩漏到名稱中
(例如介面名為 IPhone 優於 PhoneInterface
N3使用標準命名法若使用了設計模式(如 Factory, Observer),
請將其體現在名稱中,讓讀者能快速建立心智模型
N4名稱應無歧義即便長度較長,
清晰度仍優於簡短但模糊的名稱
N7描述副作用如果函式會改變狀態或做額外的事
(如 createOrReturn),名稱必須誠實反映

測試 (Tests)#

測試是系統品質的守門員。

代碼壞味道說明
T1測試不足只要還有沒被驗證的邏輯或邊界條件,測試就不算足夠
T2使用覆蓋率工具這是找出「未被測試代碼」最快的方法
T3別跳過簡單測試即使邏輯簡單,撰寫測試的文件價值往往高於其成本
T5測試邊界條件錯誤最喜歡躲在邊界(如 0, null, 空字串)
T6錯誤往往群聚當你在某個函式發現 Bug,通常附近還有更多 Bug
T9測試要夠快只有執行快速的測試,開發者才會願意頻繁地執行它

這份清單並非教條,而是啟發。
它幫我們寫程式時保持警覺,並持續追求更整潔、專業的程式碼品質。