Java設計模式之——組合模式

組合模式簡單介紹

組合模式也稱爲部分整體模式,結構型設計模式之一,組合模式比較簡單,它將一組相似的對象看做一個對象處理,並根據一個樹狀結構來組合對象,然後提供一個統一的方法去訪問相應的對象,以此忽略掉對象與對象集合之間的差別。生活中一個比較典型的例子就是組織結構的樹狀圖,如下所示:

這裏寫圖片描述

上面是一個公司的組織結構樹狀圖,其中總公司下有行政部和研發部,而且總公司下屬還有一個子公司,雖然子公司也包含行政部和研發部,但是從總公司的角度來看子公司就是一個獨立的個體,與總公司所屬的行政部和研發部平級。在這麼一個機構中大家可以看到雖然總公司和子公司其本質不一樣,但是它在我們的組織結構中是一樣的,我們可以把它們看做一個抽象的公司,在組合模式中我們將這樣的一個擁有分支的節點稱爲枝幹構件,位於樹狀結構頂部的枝幹結構比較特殊,我們稱爲根結構件,因爲其爲整個樹狀圖的始端,同樣對於向行政部和研發部這樣沒有分支的結構,我們則稱爲葉子構件,這樣的一個結構就是組合模式的雛形。

組合模式的定義

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

組合模式的使用場景

  • 表示對象的部分-整體層次結構時;
  • 從一個整體中能夠獨立出部分模塊或功能的場景

組合模式的 UML 類圖

這裏寫圖片描述

角色介紹:

  • Component:抽象根節點,爲組合中的對象聲明接口。在適當的情況下,實現所有類共有接口的缺省行爲。聲明一個接口用於訪問和管理 Component 的子節點。可在遞歸結構中定義一個接口,用於訪問一個父節點,並在合適的情況下實現它。
  • Composite:定義有子節點的那些枝幹節點的行爲,存儲子節點,在 Component 接口中實現與子節點有關的操作。
  • Leaf:在組合中表示葉子節點對象,葉子節點沒有子節點,在組合中定義節點對象的行爲。

根據類圖我們可以得出如下一個組合模式的通用模式代碼:

/**
 * 抽象根節點
 */
public abstract class Component {
    protected String name;  //節點名

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

    /**
     * 具體的邏輯方法由子類實現
     */
    public abstract void doSomething();

    /**
     * 添加子節點
     *
     * @param child
     */
    public abstract void addChild(Component child);

    /**
     * 移除子節點
     *
     * @param child
     */
    public abstract void removeChild(Component child);

    /**
     * 獲取子節點
     *
     * @param index
     * @return
     */
    public abstract Component getChildren(int index);
}

/**
 * 具體枝幹節點
 */
public class Composite extends Component {
    private List<Component> components = new ArrayList<>();

    public Composite(String name) {
        super(name);
    }

    @Override
    public void doSomething() {
        Log.d("Component", "name:" + name);
        if (null != components) {
            for (Component c : components) {
                c.doSomething();
            }
        }
    }

    @Override
    public void addChild(Component child) {
        components.add(child);
    }

    @Override
    public void removeChild(Component child) {
        components.remove(child);
    }

    @Override
    public Component getChildren(int index) {
        return components.get(index);
    }
}

/**
 * 葉子節點
 */
public class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }

    @Override
    public void doSomething() {
        Log.d("Component", "name:" + name);
        //葉子節點沒有子節點
    }

    @Override
    public void addChild(Component child) {
        throw new UnsupportedOperationException("葉子節點沒有子節點");
    }

    @Override
    public void removeChild(Component child) {
        throw new UnsupportedOperationException("葉子節點沒有子節點");
    }

    @Override
    public Component getChildren(int index) {
        throw new UnsupportedOperationException("葉子節點沒有子節點");
    }
}

/**
 * 客戶類
 */
public class Client {
    public static void main() {
        //構造一個根節點
        Component root = new Composite("Root");

        //構造兩個枝幹節點
        Component branch1 = new Composite("Branch1");
        Component branch2 = new Composite("Branch2");

        //構造兩個葉子節點
        Component leaf1 = new Leaf("Leaf1");
        Component leaf2 = new Leaf("Leaf2");

        //將葉子節點添加至枝幹節點中
        branch1.addChild(leaf1);
        branch2.addChild(leaf2);

        //將枝幹節點添加至根節點中
        root.addChild(branch1);
        root.addChild(branch2);

        //執行方法
        root.doSomething();
    }
}

可以發現組合模式中不管是葉子節點還是枝幹節點都有着相同的結構,那也就意味着我們無法通過 getChildren 方法來得到具體的子節點的類型(具體原因是因爲枝幹節點中可以包含葉子節點或者枝幹節點,不明確要返回哪種類型,所以只能返回 枝幹的父類型 Component),而必須在方法實現的內部進行判斷。

總結

我們平時在 Android 開發的過程中組合模式的應用並不算多,組合模式更適用於對一些界面 UI 的架構設計上,當然,絕大多數情況下,這部分代碼都會由相應的程序語言提供,比如 Java 的 AWT、Android 和 IOS 的 UI 框架等,真正需要開發者去實現的不多。這裏不在累贅。

組合模式的優點:

  • 組合模式可以清楚地定義分層次的複雜對象,表示對象的全部或部分層次,它讓高層模塊忽略了層次的差異,方便對整個層次結構進行控制。
  • 高層模塊可以一致地使用一個組合結構或其中單個對象,不必關係處理的單個對象還是整個組合結構,簡化了高層模塊的代碼。
  • 在組合模式中增加新的枝幹構件和葉子構件都很方便,無須對現有類庫進行任何修改,符合“開閉原則”;
  • 組合模式爲樹形結構的面向對象實現提供了一種靈活的解決方案,通過葉子對象和枝幹對象的遞歸組合,可以形成複雜的樹形結構,但對樹形結構的控制卻非常簡單。

組合模式的缺點:

  • 在新增構件時不好對枝幹中的構件類型進行限制,不能依賴類型系統來施加這些約束,因爲在大多數情況下,它們都來自於相同的抽象層,此時,必須行時進行類型檢查來實現,這個實現過程較爲複雜。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章