1. 程式人生 > >Java 設計模式系列(九)組合模式

Java 設計模式系列(九)組合模式

ima 技術分享 client 索引 有變 int spa 初始 類型

Java 設計模式系列(九)組合模式

將對象組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得用戶對單個對象的使用具有一致性。

一、組合模式結構

技術分享圖片

  • Component: 抽象的組件對象,為組合中的對象聲明接口,讓客戶端可以通過這個接口來訪問和管理整個對象結構,可以在裏面為定義的功能提供缺省的實現。

  • Leaf: 葉子節點對象,定義和實現葉子對象的行為,不再包含其它的子節點對象。

  • Composite: 組合對象,通常會存儲子組件,定義包含子組件的那些組件的行為,並實現在組件接口中定義的與子組件有關的操作。

  • Client: 客戶端,通過組件接口來操作組合結構裏面的組件對象。

組合模式參考實現

(1)先看看組件對象的定義,示例代碼如下:

/**
 * 抽象的組件對象,為組合中的對象聲明接口,實現接口的缺省行為
 */
public abstract class Component {

    /** 示意方法,子組件對象可能有的功能方法 */
    public abstract void someOperation();

    /** 向組合對象中加入組件對象 */
    public void addChild(Component child) {
        // 缺省的實現,拋出例外,因為葉子對象沒有這個功能,或者子組件沒有實現這個功能
        throw new UnsupportedOperationException("對象不支持這個功能"
); } /** 從組合對象中移出某個組件對象 */ public void removeChild(Component child) { // 缺省的實現,拋出例外,因為葉子對象沒有這個功能,或者子組件沒有實現這個功能 throw new UnsupportedOperationException("對象不支持這個功能"); } /** 返回某個索引對應的組件對象 */ public Component getChildren(int index) { // 缺省的實現,拋出例外,因為葉子對象沒有這個功能,或者子組件沒有實現這個功能
throw new UnsupportedOperationException("對象不支持這個功能"); } }

(2)接下來看看Composite對象的定義,示例代碼如下:

/**
 * 組合對象,通常需要存儲子對象,定義有子部件的部件行為,
 * 並實現在Component裏面定義的與子部件有關的操作
 */
public class Composite extends Component {
    /**
     * 用來存儲組合對象中包含的子組件對象
     */
    private List<Component> childComponents = null;

    /** 示意方法,通常在裏面需要實現遞歸的調用 */
    public void someOperation() {
        if (childComponents != null){
            for(Component c : childComponents){
                //遞歸的進行子組件相應方法的調用
                c.someOperation();
            }
        }
    }
    public void addChild(Component child) {
        //延遲初始化
        if (childComponents == null) {
            childComponents = new ArrayList<Component>();
        }
        childComponents.add(child);
    }

    public void removeChild(Component child) {
        if (childComponents != null) {
            childComponents.remove(child);
        }
    }

    public Component getChildren(int index) {
        if (childComponents != null){
            if(index>=0 && index<childComponents.size()){
                return childComponents.get(index);
            }
        }
        return null;
    }
}

(3)該來看葉子對象的定義了,相對而言比較簡單,示例代碼如下:

/**
 * 葉子對象,葉子對象不再包含其它子對象
 */
public class Leaf extends Component {
    /** 示意方法,葉子對象可能有自己的功能方法 */
    public void someOperation() {
        // do something
    }

}

(4)對於Client,就是使用Component接口來操作組合對象結構,由於使用方式千差萬別,這裏僅僅提供一個示範性質的使用,順便當作測試代碼使用,示例代碼如下:

public class Client {
    public static void main(String[] args) {
        //定義多個Composite對象
        Component root = new Composite();
        Component c1 = new Composite();
        Component c2 = new Composite();
        //定義多個葉子對象
        Component leaf1 = new Leaf();
        Component leaf2 = new Leaf();
        Component leaf3 = new Leaf();

        //組和成為樹形的對象結構
        root.addChild(c1);
        root.addChild(c2);
        root.addChild(leaf1);

        c1.addChild(leaf2);
        c2.addChild(leaf3);

        //操作Component對象
        Component o = root.getChildren(1);
        System.out.println(o);
    }
}

二、父組件引用

技術分享圖片

(1) Component

public abstract class Component {
    /**
     * 記錄父組件對象
     */
    private Component parent = null;

    /**
     * 獲取一個組件的父組件對象
     * @return 一個組件的父組件對象
     */
    public Component getParent() {
        return parent;
    }
    /**
     * 設置一個組件的父組件對象
     * @param parent 一個組件的父組件對象
     */
    public void setParent(Component parent) {
        this.parent = parent;
    }
    /**
     * 返回某個組件的子組件對象
     * @return 某個組件的子組件對象
     */
    public List<Component> getChildren() {
        throw new UnsupportedOperationException("對象不支持這個功能");
    }

    /*-------------------以下是原有的定義----------------------*/

    /**
     * 輸出組件自身的名稱
     */
    public abstract void printStruct(String preStr);

    /**
     * 向組合對象中加入組件對象
     * @param child 被加入組合對象中的組件對象
     */
    public void addChild(Component child) {
        // 缺省的實現,拋出例外,因為葉子對象沒有這個功能,或者子組件沒有實現這個功能
        throw new UnsupportedOperationException("對象不支持這個功能");
    }

    /**
     * 從組合對象中移出某個組件對象
     * @param child 被移出的組件對象
     */
    public void removeChild(Component child) {
        // 缺省的實現,拋出例外,因為葉子對象沒有這個功能,或者子組件沒有實現這個功能
        throw new UnsupportedOperationException("對象不支持這個功能");
    }

    /**
     * 返回某個索引對應的組件對象
     * @param index 需要獲取的組件對象的索引,索引從0開始
     * @return 索引對應的組件對象
     */
    public Component getChildren(int index) {
        throw new UnsupportedOperationException("對象不支持這個功能");
    }
}

(2) Leaf

public class Leaf extends Component {
    /**
     * 葉子對象的名字
     */
    private String name = "";

    /**
     * 構造方法,傳入葉子對象的名字
     * @param name 葉子對象的名字
     */
    public Leaf(String name){
        this.name = name;
    }

    /**
     * 輸出葉子對象的結構,葉子對象沒有子對象,也就是輸出葉子對象的名字
     * @param preStr 前綴,主要是按照層級拼接的空格,實現向後縮進
     */
    public void printStruct(String preStr){
        System.out.println(preStr+"-"+name);
    }
}

(3) Composite

public class Composite extends Component{

    public void addChild(Component child) {
        //延遲初始化
        if (childComponents == null) {
            childComponents = new ArrayList<Component>();
        }
        childComponents.add(child);

        //添加對父組件的引用
        child.setParent(this);
    }

    public void removeChild(Component child) {
        if (childComponents != null) {
            //查找到要刪除的組件在集合中的索引位置
            int idx = childComponents.indexOf(child);
            if (idx != -1) {
                //先把被刪除的商品類別對象的父商品類別,設置成為被刪除的商品類別的子類別的父商品類別
                for(Component c : child.getChildren()){
                    //刪除的組件對象是本實例的一個子組件對象
                    c.setParent(this);
                    //把被刪除的商品類別對象的子組件對象添加到當前實例中
                    childComponents.add(c);
                }

                //真的刪除
                childComponents.remove(idx);
            }
        }
    }

    public List<Component> getChildren() {
        return childComponents;
    }

    /*-------------------以下是原有的實現,沒有變化----------------------*/

    /**
     * 用來存儲組合對象中包含的子組件對象
     */
    private List<Component> childComponents = null;

    /**
     * 組合對象的名字
     */
    private String name = "";

    /**
     * 構造方法,傳入組合對象的名字
     * @param name 組合對象的名字
     */
    public Composite(String name){
        this.name = name;
    }

    /**
     * 輸出組合對象自身的結構
     * @param preStr 前綴,主要是按照層級拼接的空格,實現向後縮進
     */
    public void printStruct(String preStr){
        //先把自己輸出去
        System.out.println(preStr+"+"+this.name);
        //如果還包含有子組件,那麽就輸出這些子組件對象
        if(this.childComponents!=null){
            //然後添加一個空格,表示向後縮進一個空格
            preStr+=" ";
            //輸出當前對象的子對象了
            for(Component c : childComponents){
                //遞歸輸出每個子對象
                c.printStruct(preStr);
            }
        }
    }
}

三、總結

(1) 觀察者模式的本質

組合模式的本質: 統一葉子對象和組合對象。

組合模式通過把葉子對象當成特殊的組合對象看待,從而對葉子對象和組合對象一視同仁,統統當成了Component對象,有機的統一了葉子對象和組合對象。

正是因為統一了葉子對象和組合對象,在將對象構建成樹形結構的時候,才不需要做區分,反正是組件對象裏面包含其它的組件對象,如此遞歸下去;也才使得對於樹形結構的操作變得簡單,不管對象類型,統一操作。

(2) 何時選用觀察者模式

  • 如果你想表示對象的部分-整體層次結構,可以選用組合模式,把整體和部分的操作統一起來,使得層次結構實現更簡單,從外部來使用這個層次結構也簡單。

  • 如果你希望統一的使用組合結構中的所有對象,可以選用組合模式,這正是組合模式提供的主要功能。

(3) 觀察者模式的優缺點

  • 定義了包含基本對象和組合對象的類層次結構

    在組合模式中,基本對象可以被組合成更復雜的組合對象,而組合對象又可以組合成更復雜的組合對象,可以不斷地遞歸組合下去,從而構成一個統一的組合對象的類層次結構

  • 統一了組合對象和葉子對象

    在組合模式中,可以把葉子對象當作特殊的組合對象看待,為它們定義統一的父類,從而把組合對象和葉子對象的行為統一起來

  • 簡化了客戶端調用

    組合模式通過統一組合對象和葉子對象,使得客戶端在使用它們的時候,就不需要再去區分它們,客戶不關心使用的到底是什麽類型的對象,這就大大簡化了客戶端的使用

  • 更容易擴展

    由於客戶端是統一的面對Component來操作,因此,新定義的Composite或Leaf子類能夠很容易的與已有的結構一起工作,而客戶端不需要為增添了新的組件類而改變

  • 很難限制組合中的組件類型

    容易增加新的組件也會帶來一些問題,比如很難限制組合中的組件類型。這在需要檢測組件類型的時候,使得我們不能依靠編譯期的類型約束來完成,必須在運行期間動態檢測。


每天用心記錄一點點。內容也許不重要,但習慣很重要!

Java 設計模式系列(九)組合模式