🎯 什麼情境該想到我
當你「面對的是樹狀、可以無限巢狀的結構(檔案與資料夾、UI 元件樹、選單與子選單、組織圖),而且希望客戶端不必分辨『這是單一葉子還是一整個容器』,用同一套呼叫就能處理」的時候。
⚙️ 怎麼用(步驟 / 公式)
意圖:把物件組合成樹狀結構以表示「部分—整體」的層級關係,讓客戶端能一致地對待單一物件(葉子)與物件的組合(容器)。
主要參與者 / 結構:
- Component(元件):葉子與容器共同的介面,宣告共通操作(如 operation)。
- Leaf(葉子):沒有子節點的終端物件,實作共通操作。
- Composite(組合):持有一份子 Component 清單,實作共通操作時通常會遞迴轉發給子節點;並提供 add / remove / getChild 等管理子節點的方法。
- Client:只透過 Component 介面操作整棵樹。
做法要點:
- 抽出葉子與容器都要有的操作,定義成 Component 介面。
- Leaf 直接實作;Composite 持有子節點清單,並在實作操作時遍歷子節點遞迴呼叫。
- 客戶端一律以 Component 型別操作,不需 if 判斷是葉子還是容器。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
- 核心取捨:把 add / remove 放在 Component 介面(透明性佳、客戶端統一)會讓「葉子也有 add/remove」在型別上說不通、需在葉子丟例外;只放在 Composite(型別安全)則客戶端得先判斷型別才能加子節點——透明性與型別安全難以兩全。
- 容易讓設計變得過度一般化,什麼都塞進同一介面。
- 若結構其實不是樹、或只有一兩層固定深度,直接用清單就好,不必動用此模式。
🔗 相關工具
- 裝飾者模式 Decorator(常與組合搭配,兩者都是遞迴組合物件的結構)
- 享元模式 Flyweight(可用來共享大量重複的葉子節點以省記憶體)
- 代理模式 Proxy(可作為樹中節點的替身以控制存取或延遲載入)
- 工具-針對介面編程(客戶端只依賴 Component 介面)
- 工具-優先組合而非繼承(用物件組合建構整體)
- 設計模式