設計模式《組合模式》

weixin_33912246發表於2018-08-03

引言

  上一節說了中介者模式,這話我們們說說組合模式。這個模式主要是用來表示具有“整體—部分”關係的層次結構。

示例地址

  Demo

類圖

11022069-d3e8fd3cec28600a
image

定義

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

使用場景

  表示物件的部分-整體層次結構時。
  從一個整體中能夠獨立出部分模組或功能的場景。

背景故事

  我們去服裝市場,看看服裝的分類,服裝下面有男裝、女裝。男裝下面有包含短褲、襯衫,女裝下面包含裙子、小腳褲。按照我們一半的寫法,可以這樣寫。

一般寫法

1. 子節點
/**
 * 子節點
 *
 * @author 512573717@qq.com
 * @created 2018/8/2  上午11:43.
 */
public class ChildNode {

    private String nodeName;

    public ChildNode(String nodeName) {
        this.nodeName = nodeName;
    }

    public void getNickname(String pre) {
        System.out.println(pre + "-" + nodeName);

    }
}
2. 父節點
/**
 * 父節點
 *
 * @author 512573717@qq.com
 * @created 2018/8/2  下午2:00.
 */
public class ParentNode {

    private List<ChildNode> mLeafList = new ArrayList<>();

    private List<ParentNode> mParentList = new ArrayList<>();

    private String nodeName;

    public ParentNode(String nodeName) {
        this.nodeName = nodeName;
    }

    public void addParent(ParentNode parent) {
        mParentList.add(parent);
    }

    public void addLeaf(ChildNode leaf) {
        mLeafList.add(leaf);
    }

    public void getNickname(String pre) {
        System.out.println(pre + nodeName);
        //然後新增一個空格,表示向後縮排一個空格,輸出自己包含的葉子物件
        pre += " ";
        for (ChildNode leaf : mLeafList) {
            leaf.getNickname(pre);
        }
        //輸出當前物件的子物件了
        for (ParentNode c : mParentList) {
            //遞迴輸出每個子物件
            c.getNickname(pre);
        }
    }
}
3. Client
  //定義所有的組合物件
  ParentNode root = new ParentNode("服裝");
  ParentNode c1 = new ParentNode("男裝");
  ParentNode c2 = new ParentNode("女裝");

  //定義所有的葉子物件
  ChildNode leaf1 = new ChildNode("襯衣");
  ChildNode leaf2 = new ChildNode("夾克");
  ChildNode leaf3 = new ChildNode("裙子");
  ChildNode leaf4 = new ChildNode("套裝");

  //按照樹的結構來組合組合物件和葉子物件
  root.addParent(c1);
  root.addParent(c2);

  c1.addLeaf(leaf1);
  c1.addLeaf(leaf2);
  c2.addLeaf(leaf3);
  c2.addLeaf(leaf4);

  //呼叫根物件的輸出功能來輸出整棵樹
  root.getNickname("");
4. Result
 服裝
  男裝
   -襯衣
   -夾克
  女裝
   -裙子
   -套裝
5. 存在缺點

  需要知道那個是子節點,那個是父節點。然後按照對應的節點去新增。區別對待組合物件和葉子物件,不僅讓程式變得複雜,還對功能的擴充套件也帶來不便。

組合模式實現

1. 抽象節點類
/**
 * 節點抽象類
 *
 * @author 512573717@qq.com
 * @created 2018/8/2  下午2:17.
 */
public abstract class Node {
    //節點名字
    protected String nodeName;

    public Node(String nodeName) {
        this.nodeName = nodeName;
    }

    public void setNodeName(String nodeName) {
        this.nodeName = nodeName;
    }

    // 列印節點名字
    public abstract void getNodeName(String prefix);

    //新增節點
    public void addNode(Node node) {

    }

    //刪除節點
    public void removeNode(Node node) {

    }

    //查詢節點
    public void getIndexNode(int index) {

    }

}
2. 定義父節點
/**
 * 父節點
 *
 * @author 512573717@qq.com
 * @created 2018/8/2  下午2:43.
 */
public class ParentNode extends Node {

    private List<Node> mParents = null;

    public ParentNode(String nodeName) {
        super(nodeName);
    }

    @Override
    public void getNodeName(String prefix) {

        System.out.println(prefix+nodeName);

        if (this.mParents != null) {
            prefix +=" ";
            for (Node c : mParents) {
                //遞迴輸出每個子物件
                c.getNodeName(prefix);
            }
        }
    }

    @Override
    public void addNode(Node parent) {
        if (mParents == null) {
            mParents = new ArrayList<Node>();
        }
        mParents.add(parent);
    }
}
3. 定義子節點
/**
 * 子節點
 *
 * @author 512573717@qq.com
 * @created 2018/8/2  下午2:25.
 */
public class ChildNode extends Node {

    public ChildNode(String nodeName) {
        super(nodeName);
    }

    @Override
    public void getNodeName(String prefix) {
        System.out.println(prefix+"-"+ nodeName);
    }
}
4. Client
  Node root = new ParentNode("服裝");
  Node c1 = new ParentNode("男裝");
  Node c2 = new ParentNode("女裝");

  //定義所有的葉子物件
  Node leaf1 = new ChildNode("襯衣");
  Node leaf2 = new ChildNode("夾克");
  Node leaf3 = new ChildNode("裙子");
  Node leaf4 = new ChildNode("套裝");

  //按照樹的結構來組合組合物件和葉子物件
  root.addNode(c1);
  root.addNode(c2);
  c1.addNode(leaf1);
  c1.addNode(leaf2);
  c2.addNode(leaf3);
  c2.addNode(leaf4);
  //呼叫根物件的輸出功能來輸出整棵樹
  root.getNodeName("");

組合模式的2種實現方式

安全性實現

  如果把管理子元件的操作定義在ParentNode中,那麼客戶在使用葉子物件的時候,就不會發生使用新增子元件或是刪除子元件的操作了,因為壓根就沒有這樣的功能,這種實現方式是安全的。

透明性實現

  如果把管理子元件的操作定義在Node中,那麼客戶端只需要面對Node,而無需關心具體的元件型別,這種實現方式就是透明性的實現。上面的示例就是透明性的。

總結

  通過使用組合模式,把一個“部分-整體”的層次結構表示成了物件樹的結構,這樣一來,客戶端就無需再區分操作的是組合物件還是葉子物件了,對於客戶端而言,操作的都是元件物件。

參考連結

  組合模式

相關文章