一、引言
在軟件開發中,我們經常會遇到處理簡單對象和復合對象的情況,例如操作系統中文件目錄的處理,目錄可以包含單獨的文件,也可以包括文件夾,而文件夾里又包含着文件,如此遞歸下去或者說是分級數據結構。由於簡單對象和復合對象在功能上的區別,導致在操作過程中必須區分簡單對象和復合對象,這樣導致客戶端調用時帶來不必要的麻煩,作為客戶,希望能夠始終如一的對待簡單對象和復雜對象。組合模式就是解決這個問題的
二、組合模式
定義:將對象組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得用戶對單個對象和組合對象的使用具有一致性。
來看看組合模式的基本代碼結構
abstract class Component { protected string name; public Component(string name) { this.name = name; } public abstract void Add(Component component); public abstract void Remove(Component component); public abstract void Display(int depth); }
//Leaf在組合中表示葉節點對象 class Leaf : Component { public Leaf(string name) : base(name) { } //由於葉子沒有在增加分枝和樹葉,所以ADD與Remove方法實現它沒有意義; //但可以消除葉節點和枝節點在抽象層次的區別,它們具備完全一致的接口 public override void Add(Component component) { Console.WriteLine("葉節點不允許添加樹枝樹葉節點"); } public override void Display(int depth) { Console.WriteLine(new String('-',depth)+name); } public override void Remove(Component component) { Console.WriteLine("由於葉節點沒有子節點,這里移除沒有任何意義"); } }
class Composite : Component { private List<Component> Children = new List<Component>(); public Composite(string name) : base(name) { } public override void Add(Component component) { Children.Add(component); } //顯示枝節點名稱並對其下級進行遍歷 public override void Display(int depth) { Console.WriteLine(new String('-',depth)+name); foreach (Component component in Children) { component.Display(depth + 2); } } public override void Remove(Component component) { Children.Remove(component); } }
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(0); Console.Read(); }
結果如下圖所示:
透明方式:在Component中聲明所有管理子對象的方法,這樣實現Component的子類都有Add和Remove方法,這樣做的好處是葉節點和枝節點對於外界沒有區別,它們具備完全一致的接口,但問題也很明顯,Leaf本身沒有Add和Remove方法,實現它是沒有意義的
安全方式:在Component中不去聲明Add和Remove方法,那么子類Leaf就不需要實現它,而是在Composite中聲明所有管理子類的方法,不過由於不夠透明,樹葉類和樹枝類不具有相同的接口,客戶端調用需要相應的判斷,帶來了不便
下面是大話設計模式中公司管理系統的例子:
public abstract class Company { protected string Name; public Company(string name) { this.Name = name; } public abstract void Add(Company company); public abstract void Remove(Company company); 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 company) { Children.Add(company); } public override void Display(int depth) { Console.WriteLine(new String('-',depth)+Name); foreach (Company com in Children) { com.Display(depth+2); } } public override void LineOfDuty() { foreach (Company com in Children) { com.LineOfDuty(); } } public override void Remove(Company company) { Children.Remove(company); } }
//人力資源部類 樹葉節點 class HRDepartment:Company { public HRDepartment(string name) :base(name) { } public override void Add(Company company) { } public override void Display(int depth) { Console.WriteLine(new String('-',depth)+Name); } public override void LineOfDuty() { Console.WriteLine($"{Name}員工招聘培訓管理"); } public override void Remove(Company company) { } }
//財務部類 樹葉節點 class FinanceDepartment : Company { public FinanceDepartment(string name) : base(name) { } public override void Add(Company company) { } public override void Display(int depth) { Console.WriteLine(new String('-',depth)+Name); } public override void LineOfDuty() { Console.WriteLine($"{Name}公司財務收支管理"); } public override void Remove(Company company) { } }
static void Main(string[] args) { ConcreteCompany root = new ConcreteCompany("北京總公司"); root.Add(new HRDepartment("總公司人力資源部")); root.Add(new FinanceDepartment("總公司財務部")); ConcreteCompany comp = new ConcreteCompany("上海華東分公司"); comp.Add(new HRDepartment("華東分公司人力資源部")); comp.Add(new FinanceDepartment("華東分公司財務部")); root.Add(comp); ConcreteCompany comp1 = new ConcreteCompany("南京分公司"); comp1.Add(new HRDepartment("南京人力資源部")); comp1.Add(new FinanceDepartment("南京財務部")); comp.Add(comp1); ConcreteCompany comp2 = new ConcreteCompany("杭州分公司"); comp2.Add(new HRDepartment("杭州人力資源部")); comp2.Add(new FinanceDepartment("杭州財務部")); comp.Add(comp2); Console.WriteLine("\n結構圖"); root.Display(0); Console.WriteLine("\n職責圖"); root.LineOfDuty(); Console.Read(); }
運行結果如下圖:
使用場景:
1.想表示對象部分-整體層次結構;
2.希望用戶忽略單個對象和組合對象的不同,統一的使用組合結構中的所有對象。
優點:
1.高層模塊不需要關心處理的是單個對象還是復合對象;客戶程序可以像處理單個對象一樣處理復合對象,將客戶程序與復合對象容器結構解耦
2.更容易往組合對象中添加新的構件,只需要找父節點即可
缺點:設計更加復雜,使得客戶需要更多時間理清類之間的層次關系
關於組合模式的學習就到此結束了,希望能夠幫到你,若有不足,歡迎斧正,感謝您的閱讀。