设计模式详解(十)——组合模式

一、场景问题

设计出省、市、区机构的区域程序代码。省市区结构如下

安徽省
--合肥市
----肥东县
--芜湖市
----三山区
浙江省
--杭州市
----滨江区
--温州市
----永嘉县
江苏省
--镇江市
----京口区

二、解决方案

1、传统解决方案

1.1、实现思路

定义省市区/县三个类,省包含市,市包含区/县。

  • 代码展示

    • 区/县

      public class District {
          /**
           * 名称
           */
          private String name;
      
          public District(String name) {
              this.name = name;
          }
      
          /**
           * 区信息
           * @author xianzilei
           **/
          public void print() {
              System.out.println("----"+name);
          }
      }
      
    • public class City {
          /**
           * 名称
           */
          private String name;
          /**
           * 包含的区县信息
           */
          private List<District> districtList = new ArrayList<>();
      
          public City(String name) {
              this.name = name;
          }
      
          /**
           * 添加区/县
           *
           * @author xianzilei
           **/
          public void add(District district) {
              districtList.add(district);
          }
      
          /**
           * 市信息
           * @author xianzilei
           **/
          public void print() {
              System.out.println("--" + name);
              for (District district : districtList) {
                  district.print();
              }
          }
      }
      
    • public class Province {
          /**
           * 名称
           */
          private String name;
          /**
           * 包含的市信息
           */
          private List<City> cityList = new ArrayList<>();
      
          public Province(String name) {
              this.name = name;
          }
      
          /**
           * 添加市
           *
           * @author xianzilei
           **/
          public void add(City city) {
              cityList.add(city);
          }
      
          /**
           * 省信息
           *
           * @author xianzilei
           **/
          public void print() {
              System.out.println(name);
              for (City city : cityList) {
                  city.print();
              }
          }
      }
      
  • 客户端测试

    public static void main(String[] args) {
        //区/县
        District district1 = new District("肥东县");
        District district2 = new District("瑶海区");
        District district3 = new District("三山区");
        //市
        City city1 = new City("合肥市");
        city1.add(district1);
        city1.add(district2);
        City city2 = new City("芜湖市");
        city2.add(district3);
        //省
        Province province = new Province("安徽省");
        province.add(city1);
        province.add(city2);
    
        //打印信息
        province.print();
    }
    

    输出

    安徽省
    --合肥市
    ----肥东县
    ----瑶海区
    --芜湖市
    ----三山区
    

1.2、传统方式实现的优缺点

  • 优点:实现简单,代码易读。
  • 缺点:缺点很明显,我们必须区分不同层级的省市区,可能这个例子不太形象,我们用分类来举例。每个分类都可能会有子分类,即可能为叶子节点,也可能为非叶子节点。但是这些节点其实是同样的,没有必要区别开来,这样可能会不利于扩展,操作起来也可能不够方便。

2、代理模式方案

明白了上面写法的弊端,我们将这省市区看成一种类型,使用抽象类或接口聚合公共部分。

2.1、代码实现

  • 定义抽象区域类

    public abstract class RegionComposite {
        /**
         * 区域名称
         */
        private String name;
    
        protected RegionComposite(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        /**
         * 新增子区域信息
         * @author xianzilei
         **/
        protected void add(RegionComposite regionComposite) {
            throw new UnsupportedOperationException();
        }
    
        /**
         * 移除子区域信息
         * @author xianzilei
         **/
        protected void remove(RegionComposite regionComposite) {
            throw new UnsupportedOperationException();
        }
    
        /**
         * 打印区域信息
         *
         * @author xianzilei
         **/
        public abstract void print();
    }
    
  • 省市区代码修改

    • public class Province extends RegionComposite {
          /**
           * 子区域信息
           */
          private List<RegionComposite> childList = new ArrayList<RegionComposite>();
      
          public Province(String name) {
              super(name);
          }
      
          @Override
          protected void add(RegionComposite regionComposite) {
              childList.add(regionComposite);
          }
      
          @Override
          protected void remove(RegionComposite regionComposite) {
              childList.remove(regionComposite);
          }
      
          @Override
          public void print() {
              System.out.println(getName());
              for (RegionComposite regionComposite : childList) {
                  regionComposite.print();
              }
          }
      }
      
    • public class City extends RegionComposite {
          /**
           * 子区域信息
           */
          private List<RegionComposite> childList = new ArrayList<RegionComposite>();
      
          public City(String name) {
              super(name);
          }
      
          @Override
          protected void add(RegionComposite regionComposite) {
              childList.add(regionComposite);
          }
      
          @Override
          protected void remove(RegionComposite regionComposite) {
              childList.remove(regionComposite);
          }
      
          @Override
          public void print() {
              System.out.println("--" + getName());
              for (RegionComposite regionComposite : childList) {
                  regionComposite.print();
              }
          }
      }
      
    • 区/县

      public class District extends RegionComposite {
      
          public District(String name) {
              super(name);
          }
      
          @Override
          public void print() {
              System.out.println("----" + getName());
          }
      }
      
  • 客户端测试

    public static void main(String[] args) {
        //区/县
        RegionComposite district1 = new District("肥东县");
        RegionComposite district2 = new District("瑶海区");
        RegionComposite district3 = new District("三山区");
        //市
        RegionComposite city1 = new City("合肥市");
        city1.add(district1);
        city1.add(district2);
        City city2 = new City("芜湖市");
        city2.add(district3);
        //省
        RegionComposite province = new Province("安徽省");
        province.add(city1);
        province.add(city2);
    
        //打印信息
        province.print();
    }
    

    输出

    安徽省
    --合肥市
    ----肥东县
    ----瑶海区
    --芜湖市
    ----三山区
    

2.2、方案解析

组合模式将组合对象和叶子对象统一起来了,使得用户在操作的时候无需区分类型,可以将操作统一化。

三、模式剖析

1、模式定义

组合模式(Composite Pattern):将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性

2、模式结构

组合模式一般包含如下角色

  • Component抽象构件角色

    为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成

    abstract class Component {
        //增加成员
        public abstract void add(Component c); 
        //删除成员
        public abstract void remove(Component c); 
        //获取成员
        public abstract Component getChild(int i); 
        //业务方法
        public abstract void operation();  
    }
    
  • Leaf树叶构件角色

    组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口

    class Leaf extends Component {
        public void add(Component c) {
            //抛出异常或错误提示
        }
    
        public void remove(Component c) {
            //抛出异常或错误提示
        }
    
        public Component getChild(int i) {
            //抛出异常或错误提示
        }
        
        public void operation() {
            //叶子构件具体业务方法的实现
        }
    }
    
  • Composite树枝构件角色

    组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 add()、remove()、getChild() 等方法

    class Composite extends Component {
        //子构建集合
        private ArrayList<Component> list = new ArrayList<Component>();
    
        public void add(Component c) {
            list.add(c);
        }
    
        public void remove(Component c) {
            list.remove(c);
        }
    
        public Component getChild(int i) {
            return (Component)list.get(i);
        }
    
        public void operation() {
            //容器构件具体业务方法的实现
            //递归调用成员构件的业务方法
            for(Component component:list) {
                component.operation();
            }
        }   
    }
    

3、模式结构图

4、优缺点

  • 优点
    • 可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制
    • 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码
    • 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”
    • 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单
  • 缺点
    • 增加新构件时很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。

5、使用场景

  • 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们
  • 在一个使用面向对象语言开发的系统中需要处理一个树形结构
  • 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值