设计模式《组合模式》

引言

  上一节说了中介者模式,这话咱们说说组合模式。这个模式主要是用来表示具有“整体—部分”关系的层次结构。

示例地址

  Demo

类图

设计模式《组合模式》_第1张图片
image

定义

  将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

使用场景

  表示对象的部分-整体层次结构时。
  从一个整体中能够独立出部分模块或功能的场景。

背景故事

  我们去服装市场,看看服装的分类,服装下面有男装、女装。男装下面有包含短裤、衬衫,女装下面包含裙子、小脚裤。按照我们一半的写法,可以这样写。

一般写法

1. 子节点
/**
 * 子节点
 *
 * @author [email protected]
 * @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 [email protected]
 * @created 2018/8/2  下午2:00.
 */
public class ParentNode {

    private List mLeafList = new ArrayList<>();

    private List 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 [email protected]
 * @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 [email protected]
 * @created 2018/8/2  下午2:43.
 */
public class ParentNode extends Node {

    private List 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();
        }
        mParents.add(parent);
    }
}
3. 定义子节点
/**
 * 子节点
 *
 * @author [email protected]
 * @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,而无需关心具体的组件类型,这种实现方式就是透明性的实现。上面的示例就是透明性的。

总结

  通过使用组合模式,把一个“部分-整体”的层次结构表示成了对象树的结构,这样一来,客户端就无需再区分操作的是组合对象还是叶子对象了,对于客户端而言,操作的都是组件对象。

参考链接

  组合模式

你可能感兴趣的:(设计模式《组合模式》)