首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 敏捷開發核心技巧:識別與消除代碼異味

     編輯:Java 相關知識

什麼是代碼異味?

「代碼異味」是 Martin Fowler 推廣的術語,指代碼中預示潛在問題的表徵特徵。如同食物變質前散發異味,代碼異味雖非絕對錯誤,但強烈暗示設計缺陷,若不重構將導致維護成本指數級上升。

經典案例:CAD 繪圖系統的演進

初始設計:類型碼 + Switch 語句

class Shape {
    static final int TYPE_LINE = 0, TYPE_RECT = 1, TYPE_CIRCLE = 2;
    int shapeType;
    Point p1, p2; // 語義模糊:起點/左下/圓心?
    int radius;   // 僅圓形使用
}

class CADApp {
    void drawShapes(Graphics g, Shape[] shapes) {
        for (Shape s : shapes) {
            switch (s.shapeType) {
                case TYPE_LINE: g.drawLine(s.p1, s.p2); break;
                case TYPE_RECT: /* 畫四條邊 */ break;
                case TYPE_CIRCLE: g.drawCircle(s.p1, s.radius); break;
            }
        }
    }
}

四大異味識別

  1. 類型碼:shapeType 整數常數,擴展新形狀需修改多處。
  2. 條件性欄位:radius 僅對圓形有效,其他形狀浪費記憶體且語義混淆。
  3. 命名困難:p1/p2 無法同時表達「起點/終點」、「左下/右上」、「圓心/半徑」。
  4. Switch/If-Else 鏈:與類型碼共生,違反開放封閉原則(OCP)。

重構步驟:消除異味,實現穩定架構

步驟 1:以繼承取代類型碼


abstract class Shape { abstract void draw(Graphics g); }
class Line extends Shape { Point start, end; void draw(Graphics g) { g.drawLine(start, end); } }
class Rectangle extends Shape { Point lowerLeft, upperRight; void draw(Graphics g) { ... } }
class Circle extends Shape { Point center; int radius; void draw(Graphics g) { g.drawCircle(center, radius); } }

效果:消除類型碼、條件性欄位、命名模糊;但 drawShapes 變成 instanceof 鏈。

步驟 2:以多態取代條件分支

將「畫出形狀」抽象為統一協議:


// CADApp
void drawShapes(Graphics g, Shape[] shapes) {
    for (Shape s : shapes) s.draw(g); // 單一調用,零條件
}

新增三角形只需新增 Triangle implements Shape,**零修改既有代碼**,完美符合 OCP。

步驟 3:介面化頂層抽象


interface Shape { void draw(Graphics g); }
class Line implements Shape { ... }
class Rectangle implements Shape { ... }
class Circle implements Shape { ... }

另一種場景:以物件取代類型碼

用戶帳號系統:普通用戶(90天改密)、管理員(30天)、訪客(永不過期)。

問題代碼:Switch 分支僅差數值


int getPasswordMaxAge(UserAccount a) {
    switch (a.userType) {
        case NORMAL: return 90;
        case ADMIN: return 30;
        case GUEST: return Integer.MAX_VALUE;
    }
}

解決方案:策略物件封裝變化


class UserType {
    final int passwordMaxAge;
    final boolean canPrintReport;
    UserType(int age, boolean print) { this.passwordMaxAge = age; this.canPrintReport = print; }
    static final UserType NORMAL = new UserType(90, true);
    static final UserType ADMIN = new UserType(30, true);
    static final UserType GUEST = new UserType(Integer.MAX_VALUE, false);
}

class UserAccount {
    UserType type;
    UserType getType() { return type; }
}

// 調用端
int maxAge = account.getType().passwordMaxAge;
boolean canPrint = account.getType().canPrintReport;

優勢:無需建立子類,**以組合取代繼承**,靈活應對「行為相似、數值不同」的變化點。

決策指南:兩種消除類型碼的策略

策略適用場景關鍵詞
繼承/多態不同類型行為差異大、結構不同Replace Type Code with Subclasses
策略物件/組合行為相似、僅參數/數值不同Replace Type Code with State/Strategy

常見代碼異味速查表

  • ❌ 重複代碼(DRY 原則違反)
  • ❌ 過長方法/類(單一職責原則違反)
  • ❌ 過多參數(考慮引入參數物件)
  • ❌ 雙向依賴(類之間循環引用)
  • ❌ 命名困難/包含 And/Or(職責不清)
  • ❌ XXXUtil/Manager/Controller(無意義後綴,職責模糊)
  • ❌ 過多註解(代碼未自描述,應重構而非註解)
  • ❌ 條件性欄位/類型碼/Switch 鏈(本文核心主題)

總結

代碼異味是設計腐化的早期預警。通過識別異味 → 選擇重構手法 → 小步驟驗證,可將脆弱的「義大利麵代碼」轉化為穩定、可擴展、可測試的優雅架構。記住:最好的代碼是不需要修改就能擴展的代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved