美文网首页
设计模式《组合模式》

设计模式《组合模式》

作者: 天道__ | 来源:发表于2018-08-03 12:00 被阅读0次

    引言

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

    示例地址

      Demo

    类图

    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,而无需关心具体的组件类型,这种实现方式就是透明性的实现。上面的示例就是透明性的。

    总结

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

    参考链接

      组合模式

    相关文章

      网友评论

          本文标题:设计模式《组合模式》

          本文链接:https://www.haomeiwen.com/subject/acyhvftx.html