「代碼異味」是 Martin Fowler 推廣的術語,指代碼中預示潛在問題的表徵特徵。如同食物變質前散發異味,代碼異味雖非絕對錯誤,但強烈暗示設計缺陷,若不重構將導致維護成本指數級上升。
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;
}
}
}
}
shapeType 整數常數,擴展新形狀需修改多處。radius 僅對圓形有效,其他形狀浪費記憶體且語義混淆。p1/p2 無法同時表達「起點/終點」、「左下/右上」、「圓心/半徑」。
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 鏈。
將「畫出形狀」抽象為統一協議:
// CADApp
void drawShapes(Graphics g, Shape[] shapes) {
for (Shape s : shapes) s.draw(g); // 單一調用,零條件
}
新增三角形只需新增 Triangle implements Shape,**零修改既有代碼**,完美符合 OCP。
interface Shape { void draw(Graphics g); }
class Line implements Shape { ... }
class Rectangle implements Shape { ... }
class Circle implements Shape { ... }
用戶帳號系統:普通用戶(90天改密)、管理員(30天)、訪客(永不過期)。
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 |
代碼異味是設計腐化的早期預警。通過識別異味 → 選擇重構手法 → 小步驟驗證,可將脆弱的「義大利麵代碼」轉化為穩定、可擴展、可測試的優雅架構。記住:最好的代碼是不需要修改就能擴展的代碼。