zoukankan      html  css  js  c++  java
  • 设计模式解密(13)- 组合模式

    1、简介

    定义:允许你将对象组合成树形结构来表现"整体-部分"层次结构。 组合能让客户以一致的方法处理个别对象以及组合对象。

    主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

    英文:Composite

    类型:结构型

    2、类图及组成

    (引)类图:

    组成:

      ● Component(抽象构件):接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的缺省声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。

      ● Composite(容器构件):容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。

      ● Leaf(叶子构件)叶子节点对象,定义和实现叶子对象的行为,不再包含其它的子节点对象。

      PS: 组合模式的关键是定义了一个抽象构件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。同时容器对象与抽象构件类之间还建立一个聚合关联关系,在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构。

    代码结构:

    /**
     * 抽象构件角色: 抽象的组件对象,为组合中的对象声明接口,实现接口的缺省行为
     */
    public abstract class Component {
      /**
       * 向组合对象中加入组件对象
       * @param child 被加入组合对象中的组件对象
       */
      public void add(Component child) {
         // 缺省的实现,抛出例外,因为叶子对象没有这个功能,
         //或者子组件没有实现这个功能
         throw new UnsupportedOperationException("对象不支持这个功能");
      }
      
      /**
       * 从组合对象中移出某个组件对象
       * @param child 被移出的组件对象
       */
      public void remove(Component child) {
         // 缺省的实现,抛出例外,因为叶子对象没有这个功能,
         //或者子组件没有实现这个功能
         throw new UnsupportedOperationException("对象不支持这个功能");
      }
      
      /**
       * 返回某个索引对应的组件对象
       * @param index 需要获取的组件对象的索引,索引从0开始
       * @return 索引对应的组件对象
       */
      public Component getChild(int index) {
         // 缺省的实现,抛出例外,因为叶子对象没有这个功能,
         //或者子组件没有实现这个功能
         throw new UnsupportedOperationException("对象不支持这个功能");
      }
      
      /**
       * 示意方法,子组件对象可能有的功能方法
       */
      public abstract void operation();
    }
    
    /**
     * 容器构件:组合对象,通常需要存储子对象,定义有子部件的部件行为,并实现在Component里面定义的与子部件有关的操作
     */
    public class Composite extends Component {
      /**
       * 用来存储组合对象中包含的子组件对象
       */
      private List<Component> childComponents = null;
    
      public void add(Component child) {
         //延迟初始化
         if (childComponents == null) {
             childComponents = new ArrayList<Component>();
         }
         childComponents.add(child);
      }
      
      public void remove(Component child) {
          if (childComponents != null) {
             childComponents.remove(child);
          }
      }
      
      public Component getChild(int index) {
         if (childComponents != null){
             if(index>=0 && index<childComponents.size()){
                return childComponents.get(index);
             }
         }
         return null;
      }
      
      /**
       * 示意方法,通常在里面需要实现递归的调用
       */
      public void operation() {     
         if (childComponents != null){
             for(Component c : childComponents){
                //递归的进行子组件相应方法的调用
                c.operation();
             }
         }
      }
    }
    
    /**
    * 叶子构件:叶子对象,叶子对象不再包含其它子对象
    */
    public class Leaf extends Component {
      /**
       * 示意方法,叶子对象可能有自己的功能方法
       */
      public void operation() {
         // do something
      }
    }

    3、实例引入

    我们用组合模式实现简单的组织架构:

    + 吊炸天股份有限公司
      + 北京分公司
        - 研发部
        - 市场部
        - 省略...
      + 上海分公司
        - 市场部
        - 销售部
        - 省略...

    package com.designpattern.Composite;
    
    /**
     * 抽象构件角色
    * @author Json<<json1990@foxmail.com>>
    */
    public abstract class Component {
        /**
        * 向组合对象中加入组件对象
        * @param child 被加入组合对象中的组件对象
        */
        public void add(Component child) {
            throw new UnsupportedOperationException("对象不支持这个功能");
        }
        
        /**
        * 从组合对象中移出某个组件对象
        * @param child 被移出的组件对象
        */
        public void remove(Component child) {
            throw new UnsupportedOperationException("对象不支持这个功能");
        }
        
        /**
        * 返回某个索引对应的组件对象
        * @param index 需要获取的组件对象的索引,索引从0开始
        * @return 索引对应的组件对象
        */
        public Component getChild(int index) {
            throw new UnsupportedOperationException("对象不支持这个功能");
        }
        
        /**
        * 输出组件自身的名称
        */
        public abstract void outputSelf(String str);
    }
    package com.designpattern.Composite;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 容器构件
     * @author Json<<json1990@foxmail.com>>
     */
    public class Composite extends Component {
        /**
         * 用来存储组合对象中包含的子组件对象
         */
        private List<Component> childComponents = null;
        
        /**
         * 组合对象的名字
         */
        private String name = "";
        
        /**
         * 构造方法,传入组合对象的名字
         * @param name 组合对象的名字
         */
        public Composite(String name){
           this.name = name;
        }
    
        /**
        * 向组合对象中加入组件对象
        * @param child 被加入组合对象中的组件对象
        */
        public void add(Component child) {
           //延迟初始化
           if (childComponents == null) {
               childComponents = new ArrayList<Component>();
           }
           childComponents.add(child);
        }
        
        /**
        * 从组合对象中移出某个组件对象
        * @param child 被移出的组件对象
        */
        public void remove(Component child) {
            if (childComponents != null) {
                childComponents.remove(child);
             }     
        }
        
        /**
         * 输出组合对象自身的结构
         * @param str 前缀,主要是按照层级拼接的空格,实现向后缩进
         */
        @Override
        public void outputSelf(String str) {
           //先把自己输出去
           System.out.println(str+"+ "+this.name);
           //如果还包含有子组件,那么就输出这些子组件对象
           if(this.childComponents != null){
               //添加若干空格,表示向后缩进若干空格
               str+="    ";     
               //输出当前对象的子对象了
               for(Component c : childComponents){
                  //递归输出每个子对象
                  c.outputSelf(str);
               }
           }
        }
    }
    package com.designpattern.Composite;
    
    /**
     * 叶子
     * @author Json<<json1990@foxmail.com>>
     */
    public class Leaf extends Component {
        /**
         * 叶子对象的名字
         */
        private String name = "";
        
        /**
         * 构造方法,传入叶子对象的名字
         * @param name 叶子对象的名字
         */
        public Leaf(String name){
           this.name = name;
        }
        
        /**
         * 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字
         * @param str 前缀,主要是按照层级拼接的空格,实现向后缩进
         */
        public void outputSelf(String str) {
           System.out.println(str+"- "+name);
        }
    }

     测试:

    package com.designpattern.Composite;
    
    /**
     * 测试
     * @author Json<<json1990@foxmail.com>>
     */
    public class Client {
        public static void main(String[] args) {
            //定义所有的组合对象
            Composite root = new Composite("吊炸天股份有限公司");
            Composite c1 = new Composite("北京分公司");
            Composite c2 = new Composite("上海分公司");
            //定义所有的叶子对象
            Leaf leaf1_1 = new Leaf("研发部");
            Leaf leaf1_2 = new Leaf("市场部");
            Leaf leaf1_3 = new Leaf("省略...");
            
            Leaf leaf2_1 = new Leaf("市场部");
            Leaf leaf2_2 = new Leaf("销售部");
            Leaf leaf2_3 = new Leaf("省略...");
    
            //按照树的结构来组合组合对象和叶子对象
            root.add(c1);
            c1.add(leaf1_1);
            c1.add(leaf1_2);     
            c1.add(leaf1_3);     
            
            root.add(c2);      
            c2.add(leaf2_1);
            c2.add(leaf2_2);
            c2.add(leaf2_3);
    
            //调用根对象的输出功能来输出整棵树
            root.outputSelf("");
            
            System.out.println("--------删除节点(上海分公司撤销了)↓↓↓----------");
            root.remove(c2);
            root.outputSelf("");
        }
    }

     结果:

    + 吊炸天股份有限公司
        + 北京分公司
            - 研发部
            - 市场部
            - 省略...
        + 上海分公司
            - 市场部
            - 销售部
            - 省略...
    --------删除节点(上海分公司撤销了)↓↓↓----------
    + 吊炸天股份有限公司
        + 北京分公司
            - 研发部
            - 市场部
            - 省略...

    PS:上面的例子很简单,只是实现了基本功能,在实际开发中可能遇到的情景,会比这个复杂,这里要明白组合模式的原理,化有形为无形,做到手中无剑,心中有剑;

    4、优缺点

    优点:

      1、 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。

      2、 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。

      3、 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。

      4、 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

    缺点:

      在增加新构件时很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。

    5、适用场景

      1、 在对象具有部分-整体层次结构,可以选用组合模式,把整体和部分的操作统一起来,使得层次结构实现更简单,从外部来使用这个层次结构也简单。

      2、 系统中需要处理一个树形结构。

      3、 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。

      4、 想统一的使用组合结构中的所有对象,可以选用组合模式,这正是组合模式提供的主要功能。

      实际应用:
          XML解析
          组织结构树处理
          文件系统设计
          ......

    6、总结

    组合模式并不难理解,它主要解决的是单一对象和组合对象在使用方式上的一致性问题。如果对象具有明显的层次结构并且想要统一地使用它们,这就非常适合使用组合模式。在Web开发中,这种层次结构非常常见,很适合使用组合模式,达到对部分和整体使用的一致性。

    PS:源码地址   https://github.com/JsonShare/DesignPattern/tree/master 

       

    PS:原文地址 http://www.cnblogs.com/JsonShare/p/7239560.html

         

  • 相关阅读:
    Linux下vi的用法
    C++程序设计语言编程风格演变史(转载)
    软件项目经理必备素质(转)
    DES加密算法的实现
    世界编程大赛一等奖作品
    AStar算法的原理及应用
    一个DirectX使用的例子
    ConsolePlayer
    自制贪吃蛇游戏
    关于安装 DirectX SDk Dec 2005 后无法编译DirectShow应用程序的问题
  • 原文地址:https://www.cnblogs.com/JsonShare/p/7239560.html
Copyright © 2011-2022 走看看