本章主軸#
延續第 3 章的 CAD/CAM 問題,本章呈現最直覺的物件導向解法:把每一種特徵都做兩個版本(V1 與 V2 各一)。這個解法能跑、能交差,但作者刻意展示它為何是個壞設計——它正是「過度依賴繼承」帶來的典型反面教材。
解法的思路#
從 Slot 推廣到所有特徵#
作者推論:如果可以解 slot,那同一招就能套用到 cutout、hole 等其他特徵。
- 為每個特徵建立一個抽象類別,例如
SlotFeature、HoleFeature、CutoutFeature - 針對 V1 與 V2 各做一個衍生類別:
V1Slot、V2Slot、V1Hole、V2Hole…… - 五種特徵 × 兩個版本 = 10 個具象類別

Figure 4-3: 第一版解法——5 種特徵 × 2 個 CAD/CAM 版本造成的類別爆炸
各衍生類別的實作差異#
- V1 系列:在物件中保存 model 編號與 ID;每次方法呼叫,就轉成一連串 V1 子程式呼叫
- V2 系列:在物件中保存對應的
OOGFeature物件參考,把請求委派給它
public class V1Slot extends SlotFeature {
public double getX1() {
return V1.getX1forSlot(myModelNumber, myID);
}
}
public class V2Slot extends SlotFeature {
public double getX1() throws Exception {
return myOogF.getX1Loc();
}
}解法達成的目標#
解法達成了一個關鍵需求:對外暴露一致的 API,讓專家系統不必關心底層用的是 V1 或 V2。
注意:作者不需要在特徵層做多型,因為專家系統本來就要知道每個特徵的具體型別才能決定加工順序。真正需要多型的,是 V1/V2 這個變動軸。
為什麼這個解法「不夠好」#
四大問題#
- 方法重複(redundancy):V1 系列的每個 getter 幾乎都長一個樣,只是呼叫不同 V1 子程式
- 混亂(messy):類別圖鋪滿一堆
V?Slot、V?Hole,凝聚的概念很弱 - 緊密耦合(tight coupling):所有特徵都間接綁在 V1/V2 上,任何 CAD/CAM 變動都會牽連一整排類別
- 弱凝聚(weak cohesion):核心職責散落在多個類別中
真正致命的:類別爆炸#
當 V3 推出時,原本 10 個類別會變成 15 個(5 個特徵 × 3 個版本)。
若再多一個變動軸(例如要支援不同單位制、不同精度),整個結構會以乘法膨脹,最終難以維護。
| 變動軸 | 類別總數的成長方式 |
|---|---|
| 特徵類型 × 版本 | 乘法(class explosion) |
| 再加一個變動軸 | 三維乘法,徹底失控 |
兩個應記取的設計教訓#
太早陷入細節是分析的陷阱#
細節最容易處理、解法也最明顯,因此分析師很容易掉進去。
但愈晚決定細節,設計品質愈高。先找到問題的本質與變動軸,再決定如何切類別。
作者承認:他達成了「共同 API」這個目標,但代價是為一切都做特例化——每來一個特例就得新增一組類別。
注意你的直覺#
「直覺」雖然不科學,卻常常是設計品質最早的指標。
當你看到一個設計就感到不對勁,那種不舒服往往是真實的——更好的解法就在某個轉角,只是還沒找到。
作者表示,他第一眼就不喜歡這個解法,但花了兩小時仍找不到更好的。問題不在勤奮度,而在於整體思考方式——這部分要等讀完設計模式後才能突破。
本章重點#
- 完全靠繼承來處理「多軸變動」會導致類別爆炸
- 「能跑」不等於「可維護」;好用的 API 也可能背負糟糕的內部結構
- 變動有不同來源時,不能用單一繼承樹同時表達——這正是後續引入 Bridge、Abstract Factory 等模式的原因
- 設計時要練習聽自己的直覺,並學會延後對細節的承諾