定義: 將物件組合成樹狀結構以表示「部分-整體」的層次關係,使客戶端對單一物件與組合物件的使用具有一致性。
核心解決問題: 讓「整體」與「部分」能被一致對待,消除客戶端區分葉節點與分支節點的邏輯判斷。

public abstract class Component
{
protected string _name;
public Component(string name)
{
_name = name;
}
public abstract void Add(Component c);
public abstract void Remove(Component c);
public abstract void Display(int depth);
}
public class Leaf : Component
{
public Leaf(string name) : base(name) { }
public override void Add(Component c)
{
Console.WriteLine("不能向葉子節點添加子節點");
}
public override void Remove(Component c)
{
Console.WriteLine("葉子節點沒有子節點");
}
public override void Display(int depth)
{
Console.WriteLine(new string('-', depth) + _name);
}
}
public class Composite : Component
{
private List<Component> _children = new List<Component>();
public Composite(string name) : base(name) { }
public override void Add(Component c)
{
_children.Add(c);
}
public override void Remove(Component c)
{
_children.Remove(c);
}
public override void Display(int depth)
{
Console.WriteLine(new string('-', depth) + _name);
foreach (Component component in _children)
{
component.Display(depth + 2);
}
}
}
static void Main(string[] args)
{
Composite root = new Composite("根節點root");
root.Add(new Leaf("根上生出的葉子A"));
root.Add(new Leaf("根上生出的葉子B"));
Composite comp = new Composite("根上生出的分支CompositeX");
comp.Add(new Leaf("分支CompositeX生出的葉子LeafXA"));
comp.Add(new Leaf("分支CompositeX生出的葉子LeafXB"));
root.Add(comp);
Composite comp2 = new Composite("分支CompositeX生出的分支CompositeXY");
comp2.Add(new Leaf("分支CompositeXY生出葉子LeafXYA"));
comp2.Add(new Leaf("分支CompositeXY生出葉子LeafXYB"));
comp.Add(comp2);
root.Add(new Leaf("根節點生成的葉子LeafC"));
Leaf leafD = new Leaf("leaf D");
root.Add(leafD);
root.Remove(leafD);
root.Display(1);
Console.Read();
}

公司組織架構如下:
總經理 ├─ 技術部經理 │ ├─ 開發人員A │ └─ 開發人員B └─ 銷售部經理 (暫無下屬,未來可擴充)

需求:計算整個組織的總薪資,以及各部門層級的薪資總和。
// 1. 共同介面
public interface IComponent
{
string Title { get; set; }
decimal Salary { get; set; }
void Add(IComponent c);
void GetCost(ref decimal salary);
}
// 2. 葉節點:一般員工
public class Component : IComponent
{
public string Title { get; set; }
public decimal Salary { get; set; }
public Component(string title, decimal salary)
{
Title = title;
Salary = salary;
}
public void Add(IComponent c)
{
Console.WriteLine("Cannot add to the leaf!");
}
public void GetCost(ref decimal salary)
{
salary += Salary;
}
}
// 3. 分支節點:管理職
public class Composite : IComponent
{
private List<IComponent> _listEmployees = new List<IComponent>();
public string Title { get; set; }
public decimal Salary { get; set; }
public Composite(string title, decimal salary)
{
Title = title;
Salary = salary;
}
public void Add(IComponent comp)
{
_listEmployees.Add(comp);
}
public void GetCost(ref decimal salary)
{
salary += this.Salary;
foreach (IComponent component in _listEmployees)
{
component.GetCost(ref salary);
}
}
}
static void Main(string[] args)
{
decimal costCEO = 0.0M;
decimal costVPD = 0.0M;
// 建立 CEO 節點
IComponent compCEO = new Composite("CEO", 500000);
// 建立 VP-Development 及開發人員
IComponent compVPDev = new Composite("VP-Development", 250000);
IComponent compDev1 = new Component("Developer1", 75000);
IComponent compDev2 = new Component("Developer2", 50000);
compVPDev.Add(compDev1);
compVPDev.Add(compDev2);
// 建立 VP-Sales (暫無下屬,但用 Composite 以利未來擴充)
IComponent compVPSales = new Composite("VP-Sales", 300000);
compCEO.Add(compVPDev);
compCEO.Add(compVPSales);
// 計算 CEO 層級總薪資 (含所有下屬)
compCEO.GetCost(ref costCEO);
Console.WriteLine(String.Format("The Cost incurred at the CEO level is {0:c} ", costCEO));
// 計算 VP-Development 層級總薪資
compVPDev.GetCost(ref costVPD);
Console.WriteLine(String.Format("The Cost incurred at the VP-Development level is {0:c} ", costVPD));
}
組合模式以樹狀結構優雅解決「部分-整體」一致性問題。無論是簡單的圖形繪製、檔案系統目錄、UI 控制項樹,還是複雜的組織架構、薪資計算、選單系統,皆為其經典應用場景。關鍵在於統一介面讓客戶端無需區分葉節點與分支節點,透過遞迴實現整體操作。