對(duì)于樹(shù)形結(jié)構(gòu),當(dāng)容器對(duì)象(如文件夾)的某一個(gè)方法被調(diào)用時(shí),將遍歷整個(gè)樹(shù)形結(jié)構(gòu),尋找也包含這個(gè)方法的成員對(duì)象(可以是容器對(duì)象,也可以是葉子對(duì)象)并調(diào)用執(zhí)行,牽一而動(dòng)百,其中使用了遞歸調(diào)用的機(jī)制來(lái)對(duì)整個(gè)結(jié)構(gòu)進(jìn)行處理。由于容器對(duì)象和葉子對(duì)象在功能上的區(qū)別,在使用這些對(duì)象的代碼中必須有區(qū)別地對(duì)待容器對(duì)象和葉子對(duì)象,而實(shí)際上大多數(shù)情況下我們希望一致地處理它們,因?yàn)閷?duì)于這些對(duì)象的區(qū)別對(duì)待將會(huì)使得程序非常復(fù)雜。組合模式為解決此類問(wèn)題而誕生,它可以讓葉子對(duì)象和容器對(duì)象的使用具有一致性。
組合模式定義如下:
組合模式(Composite Pattern):組合多個(gè)對(duì)象形成樹(shù)形結(jié)構(gòu)以表示具有“整體—部分”關(guān)系的層次結(jié)構(gòu)。組合模式對(duì)單個(gè)對(duì)象(即葉子對(duì)象)和組合對(duì)象(即容器對(duì)象)的使用具有一致性,組合模式又可以稱為“整體—部分”(Part-Whole)模式,它是一種對(duì)象結(jié)構(gòu)型模式。
在組合模式中引入了抽象構(gòu)件類 Component,它是所有容器類和葉子類的公共父類,客戶端針對(duì) Component 進(jìn)行編程。組合模式結(jié)構(gòu)如圖所示:
http://wiki.jikexueyuan.com/project/design-pattern-structurized/images/1347029718_6268.jpg" alt="組合模式結(jié)構(gòu)圖" />
在組合模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
Component(抽象構(gòu)件):它可以是接口或抽象類,為葉子構(gòu)件和容器構(gòu)件對(duì)象聲明接口,在該角色中可以包含所有子類共有行為的聲明和實(shí)現(xiàn)。在抽象構(gòu)件中定義了訪問(wèn)及管理它的子構(gòu)件的方法,如增加子構(gòu)件、刪除子構(gòu)件、獲取子構(gòu)件等。
Leaf(葉子構(gòu)件):它在組合結(jié)構(gòu)中表示葉子節(jié)點(diǎn)對(duì)象,葉子節(jié)點(diǎn)沒(méi)有子節(jié)點(diǎn),它實(shí)現(xiàn)了在抽象構(gòu)件中定義的行為。對(duì)于那些訪問(wèn)及管理子構(gòu)件的方法,可以通過(guò)異常等方式進(jìn)行處理。
組合模式的關(guān)鍵是定義了一個(gè)抽象構(gòu)件類,它既可以代表葉子,又可以代表容器,而客戶端針對(duì)該抽象構(gòu)件類進(jìn)行編程,無(wú)須知道它到底表示的是葉子還是容器,可以對(duì)其進(jìn)行統(tǒng)一處理。同時(shí)容器對(duì)象與抽象構(gòu)件類之間還建立一個(gè)聚合關(guān)聯(lián)關(guān)系,在容器對(duì)象中既可以包含葉子,也可以包含容器,以此實(shí)現(xiàn)遞歸組合,形成一個(gè)樹(shù)形結(jié)構(gòu)。
如果不使用組合模式,客戶端代碼將過(guò)多地依賴于容器對(duì)象復(fù)雜的內(nèi)部實(shí)現(xiàn)結(jié)構(gòu),容器對(duì)象內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)的變化將引起客戶代碼的頻繁變化,帶來(lái)了代碼維護(hù)復(fù)雜、可擴(kuò)展性差等弊端。組合模式的引入將在一定程度上解決這些問(wèn)題。
下面通過(guò)簡(jiǎn)單的示例代碼來(lái)分析組合模式的各個(gè)角色的用途和實(shí)現(xiàn)。對(duì)于組合模式中的抽象構(gòu)件角色,其典型代碼如下所示:
abstract class Component {
public abstract void add(Component c); //增加成員
public abstract void remove(Component c); //刪除成員
public abstract Component getChild(int i); //獲取成員
public abstract void operation(); //業(yè)務(wù)方法
}
一般將抽象構(gòu)件類設(shè)計(jì)為接口或抽象類,將所有子類共有方法的聲明和實(shí)現(xiàn)放在抽象構(gòu)件類中。對(duì)于客戶端而言,將針對(duì)抽象構(gòu)件編程,而無(wú)須關(guān)心其具體子類是容器構(gòu)件還是葉子構(gòu)件。
如果繼承抽象構(gòu)件的是葉子構(gòu)件,則其典型代碼如下所示:
class Leaf extends Component {
public void add(Component c) {
//異常處理或錯(cuò)誤提示
}
public void remove(Component c) {
//異常處理或錯(cuò)誤提示
}
public Component getChild(int i) {
//異常處理或錯(cuò)誤提示
return null;
}
public void operation() {
//葉子構(gòu)件具體業(yè)務(wù)方法的實(shí)現(xiàn)
}
}
作為抽象構(gòu)件類的子類,在葉子構(gòu)件中需要實(shí)現(xiàn)在抽象構(gòu)件類中聲明的所有方法,包括業(yè)務(wù)方法以及管理和訪問(wèn)子構(gòu)件的方法,但是葉子構(gòu)件不能再包含子構(gòu)件,因此在葉子構(gòu)件中實(shí)現(xiàn)子構(gòu)件管理和訪問(wèn)方法時(shí)需要提供異常處理或錯(cuò)誤提示。當(dāng)然,這無(wú)疑會(huì)給葉子構(gòu)件的實(shí)現(xiàn)帶來(lái)麻煩。
如果繼承抽象構(gòu)件的是容器構(gòu)件,則其典型代碼如下所示:
class Composite extends Component {
private ArrayList<Component> list = new ArrayList<Component>();
public void add(Component c) {
list.add(c);
}
public void remove(Component c) {
list.remove(c);
}
public Component getChild(int i) {
return (Component)list.get(i);
}
public void operation() {
//容器構(gòu)件具體業(yè)務(wù)方法的實(shí)現(xiàn)
//遞歸調(diào)用成員構(gòu)件的業(yè)務(wù)方法
for(Object obj:list) {
((Component)obj).operation();
}
}
}
在容器構(gòu)件中實(shí)現(xiàn)了在抽象構(gòu)件中聲明的所有方法,既包括業(yè)務(wù)方法,也包括用于訪問(wèn)和管理成員子構(gòu)件的方法,如 add()、remove() 和 getChild() 等方法。需要注意的是在實(shí)現(xiàn)具體業(yè)務(wù)方法時(shí),由于容器構(gòu)件充當(dāng)?shù)氖侨萜鹘巧?,包含成員構(gòu)件,因此它將調(diào)用其成員構(gòu)件的業(yè)務(wù)方法。在組合模式結(jié)構(gòu)中,由于容器構(gòu)件中仍然可以包含容器構(gòu)件,因此在對(duì)容器構(gòu)件進(jìn)行處理時(shí)需要使用遞歸算法,即在容器構(gòu)件的 operation() 方法中遞歸調(diào)用其成員構(gòu)件的 operation() 方法。
在組合模式結(jié)構(gòu)圖中,如果聚合關(guān)聯(lián)關(guān)系不是從 Composite 到 Component 的,而是從 Composite 到 Leaf 的,如圖所示,會(huì)產(chǎn)生怎樣的結(jié)果?
http://wiki.jikexueyuan.com/project/design-pattern-structurized/images/1347029904_5585.jpg" alt="組合模式思考題結(jié)構(gòu)圖" />