「繼承是 OO 三大支柱」vs「組合優於繼承」,這對看似衝突的論斷,其實反映了軟體開發領域從 **GUI 密集型** 向 **業務建模/分層架構** 歷史演進的軌跡。
早期 OO 理論成型於 Smalltalk,但大規模落地是隨著 Windows 3.x / MFC / Java Swing 等 **圖形介面框架** 爆發。GUI 領域天然存在深層 is-a 體系:
Window → Control → Button / Edit …Dialog,視窗是 Frame …繼承層次極深:
DispatcherObject → DependencyObject → Visual → UIElement → FrameworkElement → Control → ContentControl → Window)。Object → Component → Container → Window → Frame → JFrame)。此時開發者經驗:「繼承好用、層次深沒關係、能複用基類邏輯」,於是寫入教科書成為鐵律。
業務邏輯前移,核心難題變成 **實體建模** 與 **持久化**:
Table per Class / Joined / Single Table 複雜策略,極大增加複雜度。於是現代 OO 理論 (GoF、Martin Fowler 等) 高舉「組合優於繼承」、「介面導向設計」,實則是針對 **領域模型層** 的矯正。
當前許多教材直接說「少用繼承」,卻不講 **何時適合繼承**,導致新手遇到明顯 is-a、行為需擴展、框架擴充點 (如 MFC CView、ASP.NET Controller) 時也不敢用繼承,反而濫用組合產生大量委派樣板碼。
| 場景 | 建議 | 理由 |
|---|---|---|
| 框架擴充點 (必須繼承基類) | ✅ 必須繼承 | 框架契約強制,如 CView::OnDraw, Servlet |
強 is-a、行為擴展、李氏替換原則成立 | ✅ 可繼承 | 如 Shape → Circle/Rect, Stream → FileStream/MemoryStream |
| 單純程式碼複用 (無語義關係) | ❌ 用組合/工具類 | 違反封裝,產生脆弱基類問題 |
| 領域實體、需持久化、跨層傳遞 | ❌ 用組合/值物件 | ORM 映射困難、序列化脆弱、層級耦合 |
| 多型行為需運行時切換 | ✅ 介面 + 組合 (策略模式) | 比繼承更靈活,符合開閉原則 |
繼承不是洪水猛獸,也不是萬能鑰匙。**理解其歷史語境、掌握適用邊界、在框架擴充與領域建模間切換思維模式**,才是成熟架構師的基本功。