當父型別與子型別在概念上根本沒有 IS-A 關係——替換性被破壞——就出現 Broken Hierarchy。
核心定義#
三種形態:
- Form 1:父類方法仍適用於子類別(雖然概念上沒有 IS-A 關係)。
- Form 2:子類別繼承的方法不適用,但子類別沒有拒絕它們。
- Form 3:子類別明確拒絕繼承來的不相關方法。
極端形式:繼承關係反向——子型別反而是父型別的泛化。
與 Rebellious Hierarchy 的關鍵差別:Broken Hierarchy 連 IS-A 都不成立;Rebellious Hierarchy 是 IS-A 成立但部分方法被拒絕。
常見成因#
- 為了重用實作而繼承:「我只想用父類別的程式碼,但事實上不是它的特化」。
- 錯誤套用繼承:把 IS-A 與其他關係混淆,甚至把繼承關係反向。
範例#
範例 1:JobStateReasons extends HashSet(Form 1)#
javax.print.attribute.standard.JobStateReasons 繼承 HashSet,文件直白地說「inherits its implementation from class java.util.HashSet」——為實作重用而繼承,並非真正的 IS-A。

Figure 6.33: JobStateReasons 的部分繼承階層
範例 2:Stack extends Vector(Form 2)#
java.util.Stack 繼承 java.util.Vector:
Stack 不是 Vector!Stack 應為 LIFO,但
Vector允許從中間插入或移除——而 Stack 子類沒有拒絕這些方法。可以對 stack 做違反語意的操作!

Figure 6.34: Stack 類別的部分繼承階層
範例 3:Properties extends Hashtable(Form 2)#
java.util.Properties 鍵值都應為 String,但 extends Hashtable<Object, Object>。文件警告:
「
put/putAll強烈不建議使用,會插入非字串鍵值。store或save在被汙染的 Properties 物件上會失敗。」

Figure 6.35: Properties 類別的部分繼承階層
範例 4:java.sql.Date / java.sql.Time(Form 3)#
兩者都繼承 java.util.Date,但:
java.sql.Date拒絕所有時間相關方法。java.sql.Time拒絕所有日期相關方法。
java.util.Date date = new java.util.Date();
date = new java.sql.Time(10, 10, 10);
date.getDate(); // 拋 IllegalArgumentException此範例同時觸發 Duplicate Abstraction(同名 Date)與 Multifaceted Abstraction(
java.util.Date同時管日期與時間)。

Figure 6.36: java.util.Date 的部分階層
範例 5:Employee extends Manager(反向繼承)#
新手以為「員工是經理的特化」(因為員工要回報給經理),把 Employee 寫成 Manager 的子類。但 getReportees()、conductPerformanceEvaluation() 等方法對非經理員工毫無意義——只能拒絕或留空。
真正的關係應該是 Manager IS-A Employee——繼承方向相反。

Figure 6.37: 反向繼承階層的範例
案例:BorderCell extends Cell#
一個自訂 GUI 控制元件中,
BorderCell看似Cell的特化(有邊框)。後來發現原始碼裡BorderCell其實是一群 cells 的容器——名字騙人。
教訓:糟糕的命名能讓設計審查也漏掉問題。
建議重構#
對 Form 1–3:套用「replace inheritance with delegation」(HAS-A 取代 IS-A)。
- 範例 2:
Stack內部持有Vector,不再繼承之。

Figure 6.38: Stack 改以委派 Vector 取代繼承的重構
- 範例 5(反向繼承):將繼承方向反過來——
Manager extends Employee。

Figure 6.39: 對反向繼承階層建議的重構
對品質屬性的衝擊#
- 理解性:強加的 IS-A 讓設計違反直覺。
- 可重用性、可變更性、可擴充性:替換性破洞使客戶端無法仰賴父型別撰寫程式碼。
- 可靠性:客戶端不知 IS-A 已破,把子型別物件指派給父型別參考時可能引發例外。
別名#
- Inappropriate use of inheritance:將不是 IS-A 的關係建為繼承。
- Containment by inheritance:繼承關係不代表合理的特化。
- Mistaken aggregates:繼承與物件組合混淆。
- Misapplying IS A:把屬性誤認為基底型別。
- Improper inheritance:用繼承代替實例化。
- Inverse abstraction hierarchies:子類別比父類別更抽象。
- Subclasses do not redefine methods:純實作繼承。
- Subclass inheriting inappropriate operations from superclass:本應該以訊息轉發處理。
實務考量:Class Adapter 樣式#
Adapter pattern ↗ 有兩種變體:
- Class Adapter:透過繼承轉換介面——常引入 Broken Hierarchy。
- Object Adapter:透過委派轉換介面——避免 Broken Hierarchy。
在某些情境(覆寫 Adaptee 行為、減少物件數、消除指標間接性)下,設計者仍可能有意選擇 Class Adapter。

Figure 6.40: Class Adapter 樣式的結構
