java合成模式之神奇的樹結構

什麼是合成模式

以下是互聯網的解釋。

合成模式屬於對象的結構模式,有時又叫做“部分——整體”模式。合成模式將對象組織到樹結構中,可以用來描述整體與部分的關系。合成模式可以使客戶端將單純元素與復合元素同等看待。

經常會出現有樹結構的情況 , 其中由單獨的對象或者單獨對象組成的合成對象組成 , 此時就需要利用一種方式來完成樹結構的構建工作 .
合成模式提供一個樹結構中所有對象的統一接口 , 規范樹中單獨對象和合成對象的構建過程 , 合成模式更像一個數據結構 .

合成模式的實現方式分為透明式和安全式 , 主要區別在於管理方法是在抽象構件中聲明, 還是直接在樹枝構件中定義.

  • 透明式 , 管理方法在抽象構件中聲明 , 同時樹葉節點需要用平庸的方式實現管理方法
  • 安全式 , 在樹枝構件中直接定義管理方法 , 這樣避免在樹葉構件中進行定義 .

設計模式和編程語言無關,但是二當傢的依然用Java語言去實戰舉例。


安全式合成模式

在這裡插入圖片描述

  • 抽象構件(Component)角色:這是一個抽象角色,它給參加組合的對象定義出公共的接口及其默認行為,可以用來管理所有的子對象。合成對象通常把它所包含的子對象當做類型為Component的對象。在安全式的合成模式裡,構件角色並不定義出管理子對象的方法,這一定義由樹枝構件對象給出。
  • 樹葉構件(Leaf)角色:樹葉對象沒有下級子對象,定義出參加組合的原始對象的行為。
  • 樹枝構件(Composite)角色:代表參加組合的有下級子對象的對象,並給出樹枝構件對象的行為。

抽象構件(Component)角色

抽象構件聲明瞭葉子和樹枝都應該有的行為。

package com.secondgod.composite;

/**
 * 抽象構件
 *
 * @author 二當傢的白帽子 https://le-yi.blog.csdn.net/
 */
public interface Component {
    /**
     * 輸出自身的名稱
     */
    void printStruct(String preStr);
}

樹葉構件(Leaf)角色

樹葉不會再有下級。

package com.secondgod.composite;

import java.text.MessageFormat;

/**
 * 樹葉
 *
 * @author 二當傢的白帽子 https://le-yi.blog.csdn.net/
 */
public class Leaf implements Component {
    /**
     * 葉子對象的名字
     */
    private String name;

    public Leaf(String name) {
        this.name = name;
    }

    @Override
    public void printStruct(String preStr) {
        System.out.println(MessageFormat.format("{0}-{1}", preStr, name));
    }
}

樹枝構件(Composite)角色

樹枝可以繼續長出樹枝或者樹葉,所以要有addChild方法。

package com.secondgod.composite;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * 樹枝
 *
 * @author 二當傢的白帽子 https://le-yi.blog.csdn.net/
 */
public class Composite implements Component {
    /**
     * 用來存儲組合對象中包含的子組件對象
     */
    private List<Component> childComponents = new ArrayList<Component>();
    /**
     * 組合對象的名字
     */
    private String          name;

    public Composite(String name){
        this.name = name;
    }

    /**
     * 聚集管理方法,增加一個子構件對象
     * @param child 子構件對象
     */
    public void addChild(Component child){
        childComponents.add(child);
    }

    @Override
    public void printStruct(String preStr) {
        // 先把自己輸出
        System.out.println(MessageFormat.format("{0}+{1}", preStr, name));

        // 如果還包含有子組件,那麼就輸出這些子組件對象
        if (this.childComponents != null) {
            // 添加兩個空格,表示向後縮進兩個空格
            preStr += "  ";
            // 輸出當前對象的子對象
            for (Component c : childComponents) {
                // 遞歸輸出每個子對象
                c.printStruct(preStr);
            }
        }
    }
}

使用

package com.secondgod.composite;

/**
 * 測試
 *
 * @author 二當傢的白帽子 https://le-yi.blog.csdn.net/
 */
public class Client {

    public static void main(String[]args){
        Composite root = new Composite("生物");
        Composite c1 = new Composite("動物");
        Composite c2 = new Composite("植物");

        Leaf leaf1 = new Leaf("貓貓");
        Leaf leaf2 = new Leaf("狗狗");
        Leaf leaf3 = new Leaf("大樹");
        Leaf leaf4 = new Leaf("小草");

        root.addChild(c1);
        root.addChild(c2);
        c1.addChild(leaf1);
        c1.addChild(leaf2);
        c2.addChild(leaf3);
        c2.addChild(leaf4);

        root.printStruct("");
    }
}

在這裡插入圖片描述

執行結果符合預期。


透明式合成模式

在這裡插入圖片描述


抽象構件(Component)角色

生長樹枝和樹葉的方法直接聲明在抽象構件裡。本例使用抽象類,其實也可以使用接口。

package com.secondgod.composite;

/**
 * 抽象構件
 *
 * @author 二當傢的白帽子 https://le-yi.blog.csdn.net/
 */
public abstract class Component {
    /**
     * 輸出自身的名稱
     */
    public abstract void printStruct(String preStr);

    /**
     * 聚集管理方法,增加一個子構件對象
     * @param child 子構件對象
     */
    public void addChild(Component child){
        /**
         * 缺省實現,拋出異常,因為葉子對象沒有此功能
         * 或者子組件沒有實現這個功能
         */
        throw new UnsupportedOperationException("對象不支持此功能");
    }
}

樹葉構件(Leaf)角色

透明式的葉子從實現抽象構件改成繼承抽象構件。如果抽象構件是接口,則需要平庸實現管理子構件的方法。

package com.secondgod.composite;

import java.text.MessageFormat;

/**
 * 樹葉
 *
 * @author 二當傢的白帽子 https://le-yi.blog.csdn.net/
 */
public class Leaf extends Component {
    /**
     * 葉子對象的名字
     */
    private String name;

    public Leaf(String name) {
        this.name = name;
    }

    @Override
    public void printStruct(String preStr) {
        System.out.println(MessageFormat.format("{0}-{1}", preStr, name));
    }
}

樹枝構件(Composite)角色

透明式的樹枝也是從實現抽象構件改為繼承抽象構件,這主要跟抽象構件是抽象類還是接口有關。

package com.secondgod.composite;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * 樹枝
 *
 * @author 二當傢的白帽子 https://le-yi.blog.csdn.net/
 */
public class Composite extends Component {
    /**
     * 用來存儲組合對象中包含的子組件對象
     */
    private List<Component> childComponents = new ArrayList<Component>();
    /**
     * 組合對象的名字
     */
    private String          name;

    public Composite(String name){
        this.name = name;
    }

    /**
     * 聚集管理方法,增加一個子構件對象
     * @param child 子構件對象
     */
    public void addChild(Component child){
        childComponents.add(child);
    }

    @Override
    public void printStruct(String preStr) {
        // 先把自己輸出
        System.out.println(MessageFormat.format("{0}+{1}", preStr, name));

        // 如果還包含有子組件,那麼就輸出這些子組件對象
        if (this.childComponents != null) {
            // 添加兩個空格,表示向後縮進兩個空格
            preStr += "  ";
            // 輸出當前對象的子對象
            for (Component c : childComponents) {
                // 遞歸輸出每個子對象
                c.printStruct(preStr);
            }
        }
    }
}

使用

客戶端在使用時,變量可以都聲明為抽象構件。

package com.secondgod.composite;

/**
 * 測試
 *
 * @author 二當傢的白帽子 https://le-yi.blog.csdn.net/
 */
public class Client {

    public static void main(String[]args){
        Component root = new Composite("生物");
        Component c1 = new Composite("動物");
        Component c2 = new Composite("植物");

        Component leaf1 = new Leaf("貓貓");
        Component leaf2 = new Leaf("狗狗");
        Component leaf3 = new Leaf("大樹");
        Component leaf4 = new Leaf("小草");

        root.addChild(c1);
        root.addChild(c2);
        c1.addChild(leaf1);
        c1.addChild(leaf2);
        c2.addChild(leaf3);
        c2.addChild(leaf4);

        root.printStruct("");
    }
}

可以看出,客戶端無需再區分操作的是樹枝對象(Composite)還是樹葉對象(Leaf)瞭;對於客戶端而言,操作的都是Component對象。


安全式和透明式

安全式:從客戶端使用合成模式上看是否更安全,如果是安全的,那麼就不會有發生誤操作的可能,能訪問的方法都是被支持的。

透明式:從客戶端使用合成模式上,是否需要區分到底是“樹枝對象”還是“樹葉對象”。如果是透明的,那就不用區分,對於客戶而言,都是Compoent對象,具體的類型對於客戶端而言是透明的,是無須關心的。因為無論樹葉還是樹枝,均符合一個固定的接口。

到底使用安全式還是透明式需要看需求,大傢看著辦吧。


以上就是java合成模式之神奇的樹結構的詳細內容,更多關於java合成模式的資料請關註WalkonNet其它相關文章!

推薦閱讀: