當繼承階層過深時,就出現 Deep Hierarchy。

核心定義#

經驗法則:階層深度超過六層即可視為過深。

過深的階層帶來:

  • 葉子類別繼承大量方法 → 行為難以預測
  • 父類別中的方法可能被多次覆寫 → 葉子層的語意不明確。
  • 父類別的欄位可能被子類別中的局部變數遮蓋(shadow)→ 難以追蹤。

Deep Hierarchy 與 Speculative Hierarchy 經常同時出現:當為了未來預留中介型別時,深度自然就會增加。但兩者本質不同。

常見成因#

  • 過度追求重用:以中介抽象「方便重用」為名,犧牲理解性。
  • 推測性泛化:未來防備同時拉長階層。

範例#

範例 1:DatagramChannel#

java.nio.channels.DatagramChannel 的:

  • 介面繼承樹深度:7
  • 類別繼承樹深度:4

總共難以一眼釐清關係。

Figure 6.23: java.nio.channels 中 DatagramChannel 的繼承階層

範例 2:SynthCheckBoxUI#

javax.swing.plaf.synth.SynthCheckBoxUI 是個深而 list-like 的階層。每層只新增少量方法,部分方法在中間層級被覆寫——理解某個被繼承方法的真實語意非常費力。

Figure 6.24: SynthCheckBoxUI 類別的繼承階層

建議重構#

套用 collapse hierarchy:移除多餘或推測性的中介抽象。

Figure 6.25: 對 Deep Hierarchy 套用 collapse hierarchy 重構

範例 1 的重構#

InterruptibleChannel 是個 marker interface,只用於 instanceof 檢查。Java 1.5 引入註解後,marker interface 可以改用 @interface,藉此縮短階層深度。

範例 2 的重構#

ButtonUI 抽象類別本身沒有任何成員——它也只用來「標記」型別。可改用註解或合併。

軼事:Deep Hierarchy 引發的記憶體問題#

一個遠端裝置監控應用為每種裝置建立一個類別,並組成階層。隨著新裝置加入,階層越來越深。 葉子物件因此繼承了祖先所有的成員變數——很多其實沒在用。每個葉子物件數百 KB,數千個物件同時存在 → 記憶體耗盡 → 效能崩潰

教訓:設計結構直接影響執行期品質

對品質屬性的衝擊#

  • 理解性:葉子類別繼承大量方法、行為難預測。
  • 可變更性與可擴充性:改父型別波及所有子孫;新增子型別也需要思考所有層級。
  • 可測試性:被覆寫多次的方法測試成本高。
  • 可靠性:方法覆寫與欄位遮蓋容易引發執行期錯誤。

別名#

  • Distorted hierarchy:階層異常窄而深。

實務考量:促進重用#

Deep Hierarchy 的確能促進重用——成熟的框架/函式庫常有深階層。設計者應在重用 vs.(理解性、可變更性、可擴充性、可靠性、效能)之間有意識地權衡