Martin Fowler 《重構》一書系統梳理了代碼壞味道。本文針對 Java 語言特性,精選 21 種高頻問題,說明成因、危害及重構方向,助力編寫整潔、可維護代碼。
成因:Copy-Paste 編程。
對策:Extract Method、Pull Up Method、Extract Class。遵循 OAOO(Once And Only Once)原則。
成因:結構化編程遺毒,多意圖混雜。
對策:Extract Method,單一職責,方法 5-15 行為宜。
成因:承擔過多職責。
對策:Extract Class/Subclass/Interface,遵循「一個類一個職責」。
成因:類內因不同原因頻繁變更(如業務邏輯變、DB Schema 變)。
對策:Split Phase、Extract Class,隔離變化維度。
成因:單一變更需修改多個類(如新增字段涉及 Entity、DAO、Service、Controller、DTO)。
對策:Move Method/Field,將相關變化聚合到同一模組。
成因:方法頻繁訪問其他類數據(getter 狂呼)。
對策:Move Method 到數據所在類,或引入 Rich Domain Model。
成因:多個數據總同時出現(參數列、字段組)。
對策:Extract Class 封裝為值對象(如 Address, Money, DateRange)。
成因:用 int/String/double 表達領域概念(金額、郵編、電話)。
對策:引入值對象(Money, Email, PostalCode),利用類型系統約束邏輯。
成因:基於常量/枚舉的 switch/if-else 鏈。
對策:Replace Conditional with Polymorphism(Strategy/State 模式),或 Map/函數式分派。
成因:新增一子類必須同步新增另一體系子類。
對策:Move Method/Field 合併體系,或用組合替代繼承。
成因:類幾乎無行為/狀態,維護成本 > 價值。
對策:Inline Class,折疊到調用者。
成因:為「未來可能」實現未用功能,唯一用戶是測試。
對策:YAGNI 原則,刪除無用代碼,需要時再加。
成因:字段僅在特定場景有效(如演算法中間變量)。
對策:Extract Class 封裝臨時狀態與行為,或改為方法局部變量。
成因:a.getB().getC().doSomething() 客戶端依賴完整對象圖結構。
對策:Hide Delegate,在中間類添加委派方法,或引入 Facade。
成因:類過度委派,接口大多數方法僅轉發。
對策:Remove Middle Man,讓客戶直接訪問真實對象。
成因:兩類互相探訪私有成員(反射、友元、同包訪問)。
對策:Move Method/Field、Extract Class、Change Bidirectional Association to Unidirectional。
成因:功能相似類擁有不同方法簽名。
對策:Rename Method、Move Method 統一協議,提取共同介面。
成因:第三方庫缺所需方法,難以修改源碼。
對策:Introduce Foreign Method(工具類)、Introduce Local Extension(子類/包裝類/適配器)。
成因:類僅有字段 + getter/setter,無行為。
對策:Encapsulate Field、Move Method 將行為遷入,或轉為 Record (JDK14+)/Value Object。
成因:子類僅使用父類極小部分功能,繼承體系設計錯誤。
對策:Replace Inheritance with Delegation,或 Push Down Method/Field。
成因:代碼不夠自解釋,需大量註釋補充。
對策:Rename/Extract Method 讓代碼成爲最好文檔,保留「為什麼」註釋而非「做什麼」。
壞味道非絕對錯誤,而是「值得關注的信號」。熟練識別並果斷重構,是從「能跑通」邁向「好維護」的關鍵飛躍。