組合模式(Composite),將對象組合成屬性結構以表示'部分-總體'的層次結構,組合模式使得用戶對單個對象和組合對象的使用具備一致性。javascript
下面給出組合模式的UML圖:html
來看組合模式的基本代碼結構:java
namespace ConsoleApplication1 { //Component爲組合中的對象聲明接口 abstract class Component { protected string name; public Component(string name) { this.name = name; } public abstract void Add(Component c); public abstract void Remove(Component c); public abstract void Display(int depth); } //Leaf在組合中表示葉節點對象 class Leaf : Component { public Leaf(string name) : base(name) { } public override void Add(Component c) //因爲葉子沒有再增長分支和樹葉,因此Add方法和Remove方法對它沒有意義, //但這樣作能夠消除葉節點和枝節點對象在抽象層次的區別,他們具有徹底一直的接口 { Console.WriteLine("葉子節點不容許再添加樹枝和樹葉"); } public override void Remove(Component c) { Console.WriteLine("葉子節點根本沒有子節點,移除方法也沒有意義"); } public override void Display(int depth) { Console.WriteLine(new String('-', depth) + name); } } //枝節點 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); } } } class Program { static void Main(string[] args) { Composite root = new Composite("root"); root.Add(new Leaf("Leaf A")); root.Add(new Leaf("Leaf B")); Composite comp = new Composite("Composite X"); comp.Add(new Leaf("Leaf XA")); comp.Add(new Leaf("Leaf XB")); root.Add(comp); Composite comp2 = new Composite("Composite XY"); comp2.Add(new Leaf("Leaf XYA")); comp2.Add(new Leaf("Leaf XYB")); comp.Add(comp2); root.Add(new Leaf("Leaf C")); Leaf leaf = new Leaf("Leaf D"); root.Add(leaf); root.Remove(leaf); root.Display(1); Console.ReadKey(); } } }
結果以下圖所示:git
至於在Component接口中的全部子類都具有了Add和Remove。這樣作的好處就是葉節點和枝節點對於外界沒有區別,他們具有徹底一致的行爲接口。但問題也很明顯,由於Leaf類自己不具有Add()、Remove()方法的功能,因此事先它是沒有意義的。不過,若是是不一致的話,那麼客戶端在調用的時候,就要先判斷這個節點是否是葉子節點才能用Add和Remove方法了。設計模式
什麼地方適合使用組合模式呢?ide
當你發現需求中是體現部分與總體層次的結構時,以及你但願用戶能夠忽略組合對象與單個對象的不一樣,統一地使用組合結構中的全部對象時,就應該考慮用組合模式了。post
下面回到《大話設計模式》中的公司管理系統的例子:學習
namespace ConsoleApplication1 { //公司類,抽象類 abstract class Company { protected string name; public Company(string name) { this.name = name; } public abstract void Add(Company c); //增長 public abstract void Remove(Company c); //移除 public abstract void Display(int depth); //顯示 public abstract void LineOfDuty(); //履行職責 } //具體公司類 class ConcreteCompany : Company { private List<Company> children = new List<Company>(); public ConcreteCompany(string name) : base(name) { } public override void Add(Company c) { children.Add(c); } public override void Remove(Company c) { children.Remove(c); } public override void Display(int depth) { Console.WriteLine(new String('-',depth) + name); foreach (Company component in children) { component.Display(depth + 2); } } //履行職責 public override void LineOfDuty() { foreach (Company component in children) { component.LineOfDuty(); } } } //人力資源部 class HRDepartment : Company { public HRDepartment(string name) : base(name) { } public override void Add(Company c) { } public override void Remove(Company c) { } public override void Display(int depth) { Console.WriteLine(new String('-', depth) + name); } public override void LineOfDuty() { Console.WriteLine("{0}員工招聘培訓管理", name); } } //財務部 class FinanceDepartment : Company { public FinanceDepartment(string name) : base(name) { } public override void Add(Company c) { } public override void Remove(Company c) { } public override void Display(int depth) { Console.WriteLine(new String('-', depth) + name); } public override void LineOfDuty() { Console.WriteLine("{0}公司財務收支管理", name); } } class Program { static void Main(string[] args) { ConcreteCompany root = new ConcreteCompany("北京總公司"); root.Add(new HRDepartment("總公司人力資源部")); //注意 此處是root.Add 說明這個節點是總公司下的 root.Add(new FinanceDepartment("總公司財務部")); ConcreteCompany comp = new ConcreteCompany("上海華東分公司"); comp.Add(new HRDepartment("華東分公司人力資源部")); //此處是comp.Add 說明這個節點是分公司下的 comp.Add(new FinanceDepartment("華東分公司財務部")); root.Add(comp); //分公司屬於總公司直接管理 ConcreteCompany comp1 = new ConcreteCompany("南京辦事處"); comp1.Add(new HRDepartment("南京辦事處人力資源部")); comp1.Add(new FinanceDepartment("南京辦事處財務部")); root.Add(comp1); //分公司屬於總公司直接管理 ConcreteCompany comp2 = new ConcreteCompany("杭州辦事處"); comp2.Add(new HRDepartment("杭州辦事處人力資源部")); comp2.Add(new FinanceDepartment("杭州辦事處財務部")); root.Add(comp2); //分公司屬於總公司直接管理 Console.WriteLine("\n結構圖:"); root.Display(1); Console.WriteLine("\n職責:"); root.LineOfDuty(); Console.ReadKey(); } } }
結果以下所示:this
這樣寫的好處呢?組合模式這樣就定義了包含人力資源部和財務部這些基本對象和分公司、辦事處等組合對象的類層次結構。基本對象能夠被組合成更復雜的對象,而這個組合對象又何以被組合,這樣不斷地遞歸下去,客戶代碼中,任何用到基本對象的地方均可以使用組合對象了。同時客戶端是不用關心究竟是處理一個葉節點仍是處理一個組合組件,也就用不着爲定義組合而寫一些選擇判斷語句了。簡單說來,組合模式讓客戶能夠一直地使用組合結構和單個對象。spa