Java結構型設計模式之組合模式詳解

組合模式

組合模式(Composite Pattern)也稱為整體-部分(Part-Whole)模式,屬於結構型模式。

它的宗旨是通過將單個對象(葉子節點)和組合對象(樹枝節點)用相同的接口進行表示,使得客戶端對單個對象和組合對象的使用具有一致性。

組合模式一般用來描述整體與部分的關系,它將對象組織到樹形結構中,最頂層的節點稱為根節點,根節點下面可以包含樹枝節點和葉子節點,樹枝節點下面又可以包含樹枝節點和葉子節點。

應用場景

1.希望客戶端可以忽略組合對象與單個對象的差異時。

2.對象層次具備整體和部分,呈樹形結構。

例如:樹形菜單,文件、文件夾的管理。

優缺點

優點:

1、高層模塊調用簡單。

2、節點自由增加。

缺點:

1.在使用組合模式時,其葉子和樹枝的聲明都是實現類,而不是接口,違反瞭依賴倒置原則。

主要角色

組合模式主要包含3個角色:

1.抽象根節點(Component)

定義系統各層次對象的共有方法和屬性,可以預先定義一些默認行為和屬性。

2.樹枝節點(Composite)

定義樹枝節點的行為,存儲子節點,組合樹枝節點和葉子節點形成一個樹形結構。

3.葉子節點(Laf)

葉子節點對象,其下再無分支,是系統層次遍歷的最小單位。

組合模式結構

分類

組合模式在具體實現上,有兩種不同的方式,分別是透明組合模式和安全組合模式。

透明組合模式將公共接口封裝到抽象根節點(Component)中,系統所有節點具備一致行為,如果當系統絕大多數層次具備相同的公共行為時,采用透明組合模式會更好。但是為剩下少數層次節點引入不需要的方法。

如果當系統各個層次差異性行為較多或者樹節點層次相對穩定時,則采用安全組合模式。

透明組合模式

透明組合模式是把所有公共方法都定義在抽象根節點中,這樣做的好處是客戶端無需分辨是葉子節點(Leaf)和樹枝節點(Composite),它們具備完全一致的接口。缺點是葉子節點(Leaf)會繼承得到一些它所不需要(管理子類操作的方法)的方法,這與設計模式接口隔離原則相違背。

創建抽象根節點

把所有可能用到的方法都定義到這個最頂層的抽象類中,但是不寫任何邏輯處理的代碼,而是直接拋出異常。

禁止使用抽象方法,否則子類必須實現,於是體現不出各個子類的差異。子類隻需要重寫有差異的方法進行覆蓋即可。

舉例:分類目錄為根節點,具體分類為樹枝節點,分類下的商品為葉子節點。

public abstract class Component {
    public String getName(Component component) {
        throw new UnsupportedOperationException("getName is not supported");
    }
    public double getPrice(Component component) {
        throw new UnsupportedOperationException("getPrice is not supported");
    }
    public String print() {
        throw new UnsupportedOperationException("print is not supported");
    }
    public boolean addChild(Component component) {
        throw new UnsupportedOperationException("addChild is not supported");
    }
    public boolean removeChild(Component component) {
        throw new UnsupportedOperationException("removeChild is not supported");
    }
    public Component getChild(int index) {
        throw new UnsupportedOperationException("getChild is not supported");
    }
}

創建樹枝節點

public class CompositeCategory extends Component {
    private String name;
    private List<Component> componentList = new ArrayList<Component>();
    public CompositeCategory(String name) {
        this.name = name;
    }
    @Override
    public String print() {
        StringBuilder builder = new StringBuilder(this.name);
        for (Component component : this.componentList) {
            if (component instanceof CompositeCategory) {
                builder.append("\n" + "+-" + component.print());
            } else {
                builder.append("\n" + "+--" + component.print());
            }
        }
        return builder.toString();
    }
    @Override
    public boolean addChild(Component component) {
        return this.componentList.add(component);
    }
    @Override
    public boolean removeChild(Component component) {
        return this.componentList.remove(component);
    }
    @Override
    public Component getChild(int index) {
        return this.componentList.get(index);
    }
}

創建葉子節點

public class CompositeProduct extends Component {
    private String name;
    private Double price;
    public CompositeProduct(String name, Double price) {
        this.name = name;
        this.price = price;
    }
    @Override
    public String print() {
        return this.name + " (¥" + this.price + "元)";
    }
    @Override
    public String getName(Component component) {
        return this.name;
    }
    @Override
    public double getPrice(Component component) {
        return this.price;
    }
}

客戶端調用

  public static void main(String[] args) {
        // 根節點
        Component root = new CompositeCategory("分類目錄");
        // 樹枝節點
        Component categoryA = new CompositeCategory("分類A");
        Component categoryB = new CompositeCategory("分類B");
        // 葉子節點
        Component productA = new CompositeProduct("productA ", 20.5);
        Component productB = new CompositeProduct("productB ", 30.5);
        Component productC = new CompositeProduct("productC", 25.5);
        root.addChild(categoryA);
        categoryA.addChild(productA);
        root.addChild(categoryB);
        categoryB.addChild(productB);
        categoryB.addChild(productC);
        System.out.println(root.print());
        System.out.println("-----------------------");
        Component child = root.getChild(1);
        System.out.println(child.print());
        System.out.println("-----------------------");
        root.removeChild(categoryA);
        System.out.println(root.print());
    }

分類目錄
+-分類A
+–productA (¥20.5元)
+-分類B
+–productB (¥30.5元)
+–productC (¥25.5元)
———————–
分類B
+–productB (¥30.5元)
+–productC (¥25.5元)
———————–
分類目錄
+-分類B
+–productB (¥30.5元)
+–productC (¥25.5元)

安全組合模式

安全組合模式是隻規定系統各個層次的最基礎的一致行為,而把組合(樹節點)本身的方法(管理子類對象的添加,刪除等)放到自身當中。

安全組合模式的好處是接口定義職責清晰,符合設計模式單一職責原側和接口隔離原則;缺點是客戶需要區分樹枝節點(Composite)和葉子節點(Leaf),這樣才能正確處理各個層次的操作,客戶端無法依賴抽象(Component),違背瞭設計模式依賴倒置原則。

創建抽象根節點

public abstract class Component {
    protected String name;
    public Component(String name) {
        this.name = name;
    }
    public abstract String print();
}

創建樹枝節點

public class CompositeCategory extends Component {
    private List<Component> componentList;
    public CompositeCategory(String name) {
        super(name);
        this.componentList = new ArrayList<Component>();
    }
    @Override
    public String print() {
        StringBuilder builder = new StringBuilder(this.name);
        for (Component component : this.componentList) {
            if (component instanceof CompositeCategory) {
                builder.append("\n" + "+-" + component.print());
            } else {
                builder.append("\n" + "+--" + component.print());
            }
        }
        return builder.toString();
    }
    public boolean addChild(Component component) {
        return this.componentList.add(component);
    }
    public boolean removeChild(Component component) {
        return this.componentList.remove(component);
    }
    public Component getChild(int index) {
        return this.componentList.get(index);
    }
}

創建葉子節點

public class CompositeProduct extends Component {
    private Double price;
    public CompositeProduct(String name, Double price) {
        super(name);
        this.price = price;
    }
    @Override
    public String print() {
        return this.name + " (¥" + this.price + "元)";
    }
    public String getName() {
        return this.name;
    }
    public double getPrice() {
        return this.price;
    }
}

客戶端調用

    public static void main(String[] args) {
        // 根節點
        CompositeCategory root = new CompositeCategory("分類目錄");
        // 樹枝節點
        CompositeCategory categoryA = new CompositeCategory("分類A");
        CompositeCategory categoryB = new CompositeCategory("分類B");
        // 葉子節點
        CompositeProduct productA = new CompositeProduct("productA", 20.5);
        CompositeProduct productB = new CompositeProduct("productB", 30.5);
        CompositeProduct productC = new CompositeProduct("productC", 25.5);
        root.addChild(categoryA);
        categoryA.addChild(productA);
        root.addChild(categoryB);
        categoryB.addChild(productB);
        categoryB.addChild(productC);
        System.out.println(root.print());
        System.out.println("-----------------------");
        Component child = root.getChild(1);
        System.out.println(child.print());
        System.out.println("-----------------------");
        root.removeChild(categoryA);
        System.out.println(root.print());
    }

分類目錄
+-分類A
+–productA (¥20.5元)
+-分類B
+–productB (¥30.5元)
+–productC (¥25.5元)
———————–
分類B
+–productB (¥30.5元)
+–productC (¥25.5元)
———————–
分類目錄
+-分類B
+–productB (¥30.5元)
+–productC (¥25.5元)

到此這篇關於Java結構型設計模式之組合模式詳解的文章就介紹到這瞭,更多相關Java組合模式內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: