設計模式之八:組合模式(Composite Pattern)
什麽是組合模式呢?簡單來說組合模式就是將對象合成樹形結構以表示“部分整體”的層次結構,組合模式使用戶對單個對象和組合對象使用具有一致性。
組合模式(Composite Pattern)有時候又叫部分-整體模式,它使我們在樹型結構的問題中,模糊了簡單元素和負責元素的概念,客戶程序可以向處理簡單元素一樣處理負責元素,從而使得客戶程序與復雜元素的的內部結構解耦。
組合模式讓你可以優化處理遞歸或分級數據結構。關於分級數據結構的一個經典例子就是電腦中的文件系統。文件系統由目錄和文件組成,所有目錄都可以有子目錄和文件。實際上文件系統就是按照遞歸來組織的,那麽就可以組合模式來描述這種結構。
適用性
以下情況下適用Composite模式:
- 想表示對象的部分—整體層次結構
- 希望用戶忽略組合對象與單個對象的不同,用戶將統一地使用組合結構中的所有對象。
組合模式解耦了客戶程序與復雜元素內部結構,從而使客戶程序可以向處理簡單元素一樣來處理復雜元素。如果你想要創建層次結構,並可以在其中以相同的方式對待所有元素,那麽組合模式就是最理想的選擇。
組成模式的實現根據實現接口的區別分為兩種形式,分別稱為安全模式和透明模式。合成模式可以不提供父對象的管理方法,但合成模式必須在合適的地方提供子對象的管理方法(如:add,remove,getchild等)。
透明方式:
作為第一種選擇,在Component裏面聲明所有的用來管理子對象的方法,包括Add(),Remove(),以及GetChild()方法。這樣做的好處是所有的構件類都有相同的接口,在客戶端看來,樹葉類對象與合成類對象的區別起碼在接口層次上消失了,客戶端可以等同的對待所有對象,這就是透明形式的合成模式。
這個選擇的缺點是不夠安全,因為樹葉類對象和合成類對象在本質上是有區別的。樹葉類對象不可能有下一個層次的對象,因此Add(),Remove(),GetChild()方法沒有意義,是在編譯時期不會出錯,而只會在運行時期出錯。
安全方式
安全方式是在Composite類裏面聲明所有的用來管理子對象的方法,這樣的做法是安全的做法,因為樹葉類型的對象根本就沒有管理子對象的方法,因此,如果客戶端對樹葉類對象使用這些方法,程序會在編譯時期出錯。 這個方式的缺點是不夠透明,因為樹葉類和合成類將具有不同的接口。
這種形式涉及到三個角色:
- 抽象構件(Component)角色:這是一個抽象角色,它給參加組合的對象定義出公共接口及默認行為,可以用來管理所有的子對象。在安全形式的合成模式裏,抽象構件角色並不定義出管理子對象的方法,這一定義由樹枝構件定義。
- 樹葉構件(Leaf)角色: 樹葉對象是沒有下級子對象的對象,定義出參加組合的原始對象的行為。
- 樹枝構件(Composite)角色: 代表參加組合的有下級子對象的對象。樹枝給出所有的管理子對象的方法,如Add(),Remove(),GetChild()等。
實現代碼:
public abstract class Component
{
protected string name;
public Component(string name)
{
this.name = name;
}
public abstract void Display(int index);
}
public class Leaf : Component
{
public Leaf(string name) : base(name) { }
public override void Display(int index)
{
Console.WriteLine("Leaf" + index);
}
}
public class Composite : Component
{
public Composite(string name) : base(name) { }
public override void Display(int index)
{
Console.WriteLine("Composite" + index);
foreach (Component c in lst)
{
c.Display( index);
}
}
IList<Component> lst = new List<Component>();
public void Add(Component ic)
{
lst.Add(ic);
}
public void Remove(Component ic)
{
lst.Remove(ic);
}
}
public class Test
{
public static void Main()
{
Composite root = new Composite("Root");
root.Add(new Leaf("Leaf A"));
root.Add(new Leaf("Leaf B"));
Composite com = new Composite("Composite X");
com.Add(new Leaf("Leaf XA"));
com.Add(new Leaf("Leaf XB"));
root.Add(com);
}
}
透明式的合成模式結構
與安全式的合成模式不同的是,透明式的模式要求所有的具體構件類,不論是樹枝構件還是樹葉構件,均有一個固定的接口。
這種形式涉及到三個角色:
- 抽象構件(Component)角色:這是一個抽象角色,它給所有參加組合的對象規定一個固定的接口,規範共有的接口及默認行為.
- 樹葉構件(Leaf)角色:代表參加組合的樹葉對象,定義出參加組合的原始對象的行為,樹葉類會給出Add(),Remove(),以及GetChild()之類的用來管理子類對象的方法的實現。
- 樹枝構件角色(Composite)角色: 代表參加組合的有子對象的對象。定義出這樣的對象的行為。
具體的實現代碼很簡單,這裏就不寫了。
使用合成模式時考慮的幾個問題:
- Composite模式采用樹形結構來實現普遍存在的對象容器,從而將“一對多”的關系轉換為“一對一”的關系,使得客戶代碼可以一致地處理對象和對象容器,無需關系處理的是單個對象還是組合的對象容器。
- 將客戶代碼與復雜的對象容器解耦是合成模式的核心思想,解耦之後,客戶代碼將與純粹的抽象接口—----非對象容器的內部實現結構發生依賴關系。
- 有時候系統需要遍歷一個樹枝結構的子構件很多次,這時候可以考慮把遍歷子構件的結構暫時存儲在父構件裏面作為緩存。
- Composite模式中,是將Add和Remove等和對象容器相關的方法定義在“表示抽象的Componont類”中,還是定義在“表示對象容器的Composite類”中,是一個關乎“透明性”和“安全性”的兩難問題,需要仔細權衡。
設計模式之八:組合模式(Composite Pattern)