當子型別直接而且間接繼承同一父型別——多餘的繼承路徑出現——就是 Multipath Hierarchy。
核心定義#
與**鑽石階層(diamond hierarchy)**的差別:
- 鑽石階層:子型別透過多條合理路徑抵達同一祖先,每條路徑都有意義(如
ByteChannel ← ReadableByteChannel ← Channel與ByteChannel ← WritableByteChannel ← Channel)。- Multipath Hierarchy:至少有一條路徑是多餘的,移除不會改變語意。

Figure 6.42: Channel 與 ByteChannel 之間的鑽石階層
常見成因#
- 忽略既有繼承路徑:定義型別時沒注意祖先已經繼承了某個介面,重複再實作一次。深階層中尤其容易發生。
範例:ConcurrentLinkedQueue#
java.util.concurrent.ConcurrentLinkedQueue:
- 繼承
java.util.AbstractQueue,AbstractQueue已實作Queue。 - 但
ConcurrentLinkedQueue又自行 implement Queue——多此一舉。

Figure 6.43: Queue 與 ConcurrentLinkedQueue 之間多餘的繼承路徑
案例:Gadget 階層的不完整重構#
一個 GUI 應用
TextGadget既繼承GadgetBase又實作ISupportInitialize。後來團隊把ISupportInitialize上提到GadgetBase,預期所有 gadget 不再各自實作——但漏改了部分 gadget 類別:它們同時透過GadgetBase與直接 implementISupportInitialize兩條路徑繼承介面。

Figure 6.44: TextGadget 類別的階層

Figure 6.45: TextGadget 階層原本預期的重構

Figure 6.46: TextGadget 不完整重構後的設計
兩個教訓:
- 重構必須完整——只做一半就會留下 Multipath Hierarchy。
- 設計變更必須廣播給整個團隊——文件化、團隊規範、code review 全到位。
建議重構#
移除多餘的繼承路徑。
- 對
ConcurrentLinkedQueue:刪除「直接 implement Queue」的宣告即可,因為它已經透過AbstractQueue繼承了Queue。

Figure 6.47: 對範例建議的重構
對品質屬性的衝擊#
- 理解性:多餘路徑使階層複雜,提高認知負擔。
- 可靠性:開發者可能不知
GadgetBase已提供標準實作 → 在子類重新提供(甚至不提供)→ 執行期錯誤。
別名#
- Degenerate inheritance:子類別直接與間接繼承同一父類別。
- Repeated inheritance:類別經多條路徑重複繼承同一祖先。
實務考量:刻意引入冗餘路徑#
軼事:一個 Java 專案的開發者故意讓低階子類別也明確
implements Serializable——因為複雜階層中追蹤是否可序列化太麻煩。設計者犧牲了此原則,換取查找的便利性。 教訓:當壞味道的代價低於它帶來的好處時,有意識地接受也是合理的設計選擇。