Composite組 合模式主要是應對這樣的問題:一類具有“容器特征”的對象——即他們在充當對象的同時,又是其他對象的容器的情況。在編寫時我們常常會造成:客戶代碼過多 地依賴于對象容器復雜的內(nèi)部實現(xiàn),對象容器內(nèi)部實現(xiàn)結(jié)構(gòu)(而非抽象接口)的變化將引起客戶代碼的頻繁變化,帶來了代碼的維護性、擴展性的弊端。
GoF《設(shè)計模式》中說到:將對象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。Composite模式使得客戶對單個對象和組合對象的使用具有一致性。
Composite組合模式結(jié)構(gòu)如下:
說道這,我覺得有一個編程中常見的場景,就是對于樹的實現(xiàn),很符合這個模式。下面我就用這個例子作一下。
首先,我們先分析對于一棵樹所包含的部分,樹干、樹枝、樹葉,其中樹干可以看成一個樹枝(就是粗了點)。那么我們就應該有兩種類實現(xiàn)Leaf(樹葉)和Limb(樹枝)。對于葉子節(jié)點和枝節(jié)點的不同在于枝節(jié)點有子樹,而葉子節(jié)點沒有子樹。為了使單個對象和組合對象的使用具有一致性,我可以將葉子節(jié)點想象成沒有子樹的枝節(jié)點。這樣我就可以得到一個抽象類,代碼如下:
public abstract class AbstractClass
{
public string name;
public ArrayList list;
public abstract void Add(AbstractClass item); //增加一個子節(jié)點
public abstract void Remove(AbstractClass item); //去掉一個子節(jié)點
public abstract string Print(); //打印當前節(jié)點
}
然后,我在對葉子節(jié)點和枝節(jié)點作不同的實現(xiàn):
枝節(jié)點:
public class Limb:AbstractClass
{
public Limb()
{
list = new ArrayList();
}
public override void Add(AbstractClass item)
{
list.Add(item);
}
public override void Remove(AbstractClass item)
{
if(list.Contains(item))
list.Remove(item);
}
public override string Print()
{
Console.Write(name + "\n");
if(list.Count != 0)
{
for(int i = 0;i<list.Count;i++)
{
Console.Write("(Parent is " + name + ")");
((AbstractClass)list[i]).Print();
}
}
return name;
}
}
葉子節(jié)點:
public class Leaf:AbstractClass
{
public Leaf()
{
list = null;
}
public override void Add(AbstractClass item)
{
}
public override void Remove(AbstractClass item)
{
}
public override string Print()
{
Console.Write(name + ",");
return this.name;
}
}
對于葉子節(jié)點來說,不需要子節(jié)點,當然也就不需要添加和刪除子節(jié)點的方法。
好,接下來,我們可以在客戶程序中組建一棵樹,來測試一下:
static void
{
AbstractClass Tree = new Limb();
GetTree(Tree);
PrintTree(Tree);
Console.Read();
}
public static void GetTree(AbstractClass Tree)
{
Tree.name = "1";
AbstractClass leaf2 = new Leaf();
leaf2.name = "2";
Tree.Add(leaf2);
AbstractClass limb3 = new Limb();
limb3.name = "3";
Tree.Add(limb3);
AbstractClass leaf4 = new Leaf();
leaf4.name = "4";
limb3.Add(leaf4);
AbstractClass leaf5 = new Leaf();
leaf5.name = "5";
limb3.Add(leaf5);
}
public static void PrintTree(AbstractClass Tree)
{
Tree.Print();
}
輸出結(jié)果如下:
1
(Parent is 1)2,(Parent is 1)3
(Parent is 3)4,(Parent is 3)5,
在組織這個樹時,的確能感覺到GoF《設(shè)計模式》中的那句話:單個對象和組合對象的使用具有一致性。當然也的確感覺到一點矛盾:對于葉子節(jié)點來說,不需要ArrayList和Add()Remove()應該不繼承才對,當然如果在代碼執(zhí)行性能可以達到要求的情況下,簡化一下編碼實現(xiàn)復雜度也是挺好的一件事。
最后在來說說Composite組合模式的幾個要點:
1、Composite模式采用樹形結(jié)構(gòu)來實現(xiàn)普遍存在的對象容器,從而將“一對多”的關(guān)系轉(zhuǎn)化為“一對一”的關(guān)系,使得客戶代碼可以一致的處理對象和對象容器,無需關(guān)心處理的是單個對象,還是組合的對象容器。
2、將“客戶代碼與復雜的對象容器結(jié)構(gòu)”解耦是Composite模式的核心思想,解耦之后,客戶代碼將與純粹的對象接口——而非對象容器的復雜內(nèi)部實現(xiàn)結(jié)構(gòu)——發(fā)生依賴關(guān)系,從而更能“應對變化”。
3、Composite模式中,是將“Add和Remove的和對象容器相關(guān)的方法”定義在“表示抽象對象的Component類”中,還是將其定義在“表示對象容器的Composite類”中,是一個關(guān)乎“透明性”和“安全性”的兩難問題,需要仔細權(quán)衡結(jié)構(gòu),這又是必須付出的代價。
4、Composite模式在具體實現(xiàn)中,可以讓父對象中的字對象反向追溯:如果父對象有頻繁的遍歷需求,可使用緩存技巧來改善效率