鍍金池/ 教程/ Java/ 樹(shù)形結(jié)構(gòu)的處理——組合模式(二)
不兼容結(jié)構(gòu)的協(xié)調(diào)——適配器模式(三)
處理多維度變化——橋接模式(二)
擴(kuò)展系統(tǒng)功能——裝飾模式(四)
<code>C#</code> 設(shè)計(jì)模式之代理模式(四)
擴(kuò)展系統(tǒng)功能——裝飾模式(二)
實(shí)現(xiàn)對(duì)象的復(fù)用——享元模式(四)
實(shí)現(xiàn)對(duì)象的復(fù)用——享元模式(五)
處理多維度變化——橋接模式(三)
深入淺出外觀模式(二)
樹(shù)形結(jié)構(gòu)的處理——組合模式(二)
處理多維度變化——橋接模式(一)
<code>C#</code> 設(shè)計(jì)模式之代理模式(二)
不兼容結(jié)構(gòu)的協(xié)調(diào)——適配器模式(二)
深入淺出外觀模式(三)
實(shí)現(xiàn)對(duì)象的復(fù)用——享元模式(三)
不兼容結(jié)構(gòu)的協(xié)調(diào)——適配器模式(四)
樹(shù)形結(jié)構(gòu)的處理——組合模式(一)
擴(kuò)展系統(tǒng)功能——裝飾模式(三)
深入淺出外觀模式(一)
處理多維度變化——橋接模式(四)
樹(shù)形結(jié)構(gòu)的處理——組合模式(五)
擴(kuò)展系統(tǒng)功能——裝飾模式(一)
不兼容結(jié)構(gòu)的協(xié)調(diào)——適配器模式(一)
實(shí)現(xiàn)對(duì)象的復(fù)用——享元模式(二)
樹(shù)形結(jié)構(gòu)的處理——組合模式(三)
<code>C#</code> 設(shè)計(jì)模式之代理模式(一)
實(shí)現(xiàn)對(duì)象的復(fù)用——享元模式(一)
樹(shù)形結(jié)構(gòu)的處理——組合模式(四)
<code>C#</code> 設(shè)計(jì)模式之代理模式(三)

樹(shù)形結(jié)構(gòu)的處理——組合模式(二)

組合模式概述

對(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)行處理。

  • Composite(容器構(gòu)件):它在組合結(jié)構(gòu)中表示容器節(jié)點(diǎn)對(duì)象,容器節(jié)點(diǎn)包含子節(jié)點(diǎn),其子節(jié)點(diǎn)可以是葉子節(jié)點(diǎn),也可以是容器節(jié)點(diǎn),它提供一個(gè)集合用于存儲(chǔ)子節(jié)點(diǎn),實(shí)現(xiàn)了在抽象構(gòu)件中定義的行為,包括那些訪問(wèn)及管理子構(gòu)件的方法,在其業(yè)務(wù)方法中可以遞歸調(diào)用其子節(jié)點(diǎn)的業(yè)務(wù)方法。

組合模式的關(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)圖" />