檔案系統中,資料夾包含檔案與子資料夾;UI 介面中,容器控件包含簡單控件與嵌套容器;組織架構中,部門包含員工與子部門。這些場景共同特徵:**樹狀結構**,且客戶端希望**統一對待葉子節點 (檔案/按鈕/員工) 與容器節點 (資料夾/面板/部門)**。組合模式透過定義共同介面,完美解決此問題。
族譜中,男性 (`Boy`) 可擁有後代 (包含男性/女性),女性 (`Girl`) 為葉子節點。所有族人共享 `Name`、`Intro()` 介面,但只有男性支援 `Add/Remove` 後代操作。這正是**透明式組合模式** (介面定義所有操作,葉子拋出異常) 的典型應用。

public abstract class FamilyMember
{
protected string _name;
public string Name => _name;
protected FamilyMember(string name) => _name = name;
// 統一介面:葉子與容器皆有
public abstract void Add(FamilyMember member);
public abstract void Remove(FamilyMember member);
public abstract void Intro(); // 遞歸展示
}
public class Boy : FamilyMember
{
private readonly List _children = new List();
public Boy(string name) : base(name) { }
public override void Add(FamilyMember member) => _children.Add(member);
public override void Remove(FamilyMember member) => _children.Remove(member);
public override void Intro()
{
Console.WriteLine($"我是: {Name} (男性,可傳宗接代)");
foreach (var child in _children)
{
child.Intro(); // 遞歸遍歷
}
}
}
public class Girl : FamilyMember
{
public Girl(string name) : base(name) { }
public override void Add(FamilyMember member)
=> throw new NotSupportedException("女性族人無法新增後代至族譜。");
public override void Remove(FamilyMember member)
=> throw new NotSupportedException("女性族人無法移除族人。");
public override void Intro()
=> Console.WriteLine($"我是: {Name} (女性,記錄於族譜)");
}
static void Main()
{
// 建構家族樹
Boy grandfather = new Boy("爺爺");
grandfather.Add(new Girl("奶奶")); // 配偶視為成員
grandfather.Add(new Girl("姑姑"));
Boy father = new Boy("爸爸");
grandfather.Add(father);
father.Add(new Boy("我"));
Boy me = (Boy)father.GetChild(0); // 假設有 GetChild 或直接引用
me.Add(new Boy("兒子"));
// 統一入口:只需呼叫根節點
Console.WriteLine("=== 完整族譜 ===");
grandfather.Intro(); // 遞歸輸出整棵樹
Console.ReadKey();
}

| 維度 | 透明式 (本例) | 安全式 |
|---|---|---|
| Component 介面 | 宣告 `Add/Remove/GetChild` | 僅宣告業務方法 (如 `Intro`) |
| Leaf 類別 | 實作管理方法並拋 `NotSupportedException` | 無管理方法,編譯期保證不被調用 |
| Composite 類別 | 實作管理方法 | 實作管理方法 |
| 客戶端調用 | 完全統一,無需區分類型 | 需轉型為 Composite 才能管理子節點 |
| 優點 | 客戶端極簡單,多態性純粹 | 類型安全,避免運行時異常 |
| 缺點 | 葉子類別被迫實作無意義方法 | 客戶端需 `if (c is Composite)` 判斷 |
| 推薦場景 | 樹結構操作頻繁、客戶端不想關心類型 | 葉子與容器行為差異大、嚴格類型安全優先 |
.NET `System.Windows.Forms.Control` 採用**透明式** (所有控件皆有 `Controls` 集合),葉子控件 `Label` 的 `Controls` 為空集合而非拋異常,是更優雅的透明式實現。
開發者只需 `parent.Controls.Add(child)`,無論 `child` 是 `Button` 還是 `Panel`,繪製、佈局、事件路由皆由框架遞歸處理,完美體現組合模式威力。
組合模式以「一致介面」馴服「遞歸結構」,是處理樹狀資料結構的基石模式。從檔案系統、UI 控件樹、組織架構、XML/JSON DOM 解析、菜單系統到遊戲場景圖,無處不在。關鍵設計決策在於:**選擇透明式還是安全式**。現代框架傾向透明式 (如 .NET Control、Java Swing Component),配合空集合而非異常處理葉子節點,兼顧多態純粹與運行時安全。掌握「抽象共同行為、組合遞歸委派、客戶端面向介面編程」三要素,即可從容應對複雜層次結構。