1 概述


    组合模式(Composite),将对象组合成树形结构来表现“整体&部分”这一层次结构。这种模式能让客户以一致的方式处理个别对象以及对象组合。

    组合内的所有对象都必须实现相同的接口,当组合结构复杂,遍历的成本太高时,就有必要实现组合节点的缓存。组合的优点是可以让客户端不再区分操作的是组合对象还是叶子对象,而是以一种统一的方式来操作。
    组合模式的组成部分有以下三个:
(1)抽象构件角色(Component):是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。这个接口可以用来管理所有的子对象。
(2)树枝构件角色(Composite):定义有子部件的那些部件的行为。在Component接口中实现与子部件有关的操作,构件和构件之间是可以嵌套的。
(3)树叶构件角色(Leaf):在组合树中表示叶节点对象,叶节点没有子节点。并在组合中定义图元对象的行为。
(4)客户角色(Client):通过component接口操纵组合部件的对象。

所以当我们的案例是树形结构或者是部分-整体的关系时,就可以考虑使用组合模式。
组合模式有两种不同的实现,分别为透明模式和安全模式,下面将详细说明一下两种实现的区别。
 先说明一下UML图中各角色的职责。Component是对象声明接口,在适当情况下,实现所有类共有接口的默认行为;Leaf是叶子节点对象,其没有子节点;Composite是树枝节点对象,用来存储部件,组合树枝节点和叶子节点形成一个树形结构。

下面这两种方式我们共用同一套客户端,先将客户端代码放上。

点击(此处)折叠或打开

  1. public class Client {

  2.     public static void main(String[] args) {
  3.         //创建根节点及其子节点
  4.         Composite root = new Composite("root");
  5.         root.add(new Leaf("Leaf A"));
  6.         root.add(new Leaf("Leaf B"));

  7.         //创建第二层节点及其子节点
  8.         Composite branch = new Composite("Composite X");
  9.         branch.add(new Leaf("Leaf XA"));
  10.         branch.add(new Leaf("Leaf XB"));
  11.         root.add(branch);
  12.         
  13.         //创建第三层节点及其子节点
  14.         Composite branch2 = new Composite("Composite XY");
  15.         branch2.add(new Leaf("Leaf XYA"));
  16.         branch2.add(new Leaf("Leaf XYB"));
  17.         branch.add(branch2);
  18.         
  19.         //创建第二层节点
  20.         root.add(new Leaf("Leaf C"));
  21.         
  22.         //创建第二层节点并删除
  23.         Leaf leaf = new Leaf("Leaf D");
  24.         root.add(leaf);
  25.         root.remove(leaf);
  26.         
  27.         //打印
  28.         root.display(1);
  29.     }
  30.     
  31. }

二、组合模式之透明模式
  透明模式是把组合使用的方法放到抽象类中,不管叶子对象还是树枝对象都有相同的结构,这样做的好处就是叶子节点和树枝节点对于外界没有区别,它们具备完全一致的行为接口。但因为Leaf类本身不具备add()、remove()方法的功能,所以实现它是没有意义的。UML结构图如下:
设计模式之组合模式-LMLPHP

点击(此处)折叠或打开

  1. public abstract class Component {
  2.     
  3.     protected String name;
  4.     
  5.     public Component(String name) {
  6.         this.name = name;
  7.     }

  8.     //增加一个叶子构件或树枝构件
  9.     public abstract void add(Component component);
  10.     
  11.     //删除一个叶子构件或树枝构件
  12.     public abstract void remove(Component component);
  13.     
  14.     //获取分支下的所有叶子构件和树枝构件
  15.     public abstract void display(int depth);
  16.     
  17. }

点击(此处)折叠或打开

  1. public class Composite extends Component {

  2.     public Composite(String name) {
  3.         super(name);
  4.     }

  5.     //构建容器
  6.     private ArrayList<Component> componentArrayList = new ArrayList<Component>();
  7.     
  8.     @Override
  9.     public void add(Component component) {
  10.         this.componentArrayList.add(component);
  11.     }

  12.     @Override
  13.     public void remove(Component component) {
  14.         this.componentArrayList.remove(component);
  15.     }

  16.     @Override
  17.     public void display(int depth) {
  18.         //输出树形结构
  19.         for(int i=0; i<depth; i++) {
  20.             System.out.print('-');
  21.         }
  22.         System.out.println(name);
  23.         
  24.         //下级遍历
  25.         for (Component component : componentArrayList) {
  26.             component.display(depth + 1);
  27.         }
  28.     }

  29. }


点击(此处)折叠或打开

  1. public class Leaf extends Component {

  2.     public Leaf(String name) {
  3.         super(name);
  4.     }

  5.     @Override
  6.     public void add(Component component) {
  7.         //空实现,抛出“不支持请求”异常
  8.         throw new UnsupportedOperationException();
  9.     }

  10.     @Override
  11.     public void remove(Component component) {
  12.         //空实现,抛出“不支持请求”异常
  13.         throw new UnsupportedOperationException();
  14.     }

  15.     @Override
  16.     public void display(int depth) {
  17.         //输出树形结构的叶子节点
  18.         for(int i=0; i<depth; i++) {
  19.             System.out.print('-');
  20.         }
  21.         System.out.println(name);
  22.     }

  23. }
通过组合模式输出一个树形结构,运行结果如下:
设计模式之组合模式-LMLPHP
三、组合模式之安全模式
  安全模式是把树枝节点和树叶节点彻底分开,树枝节点单独拥有用来组合的方法,这种方法比较安全。但由于不够透明,所以树叶节点和树枝节点将不具有相同的接口,客户端的调用需要做相应的判断,带来了不便。UML结构图如下:
设计模式之组合模式-LMLPHP
这里相比透明模式就少了add()和romove()抽象方法的声明。

点击(此处)折叠或打开

  1. public abstract class Component {
  2.     
  3.     protected String name;
  4.     
  5.     public Component(String name) {
  6.         this.name = name;
  7.     }

  8.     //获取分支下的所有叶子构件和树枝构件
  9.     public abstract void display(int depth);
  10.     
  11. }

2. Composite
  这里add()和remove()方法的实现就从继承变为了自己实现。

点击(此处)折叠或打开

  1. public class Composite extends Component {

  2.     public Composite(String name) {
  3.         super(name);
  4.     }

  5.     //构建容器
  6.     private ArrayList<Component> componentArrayList = new ArrayList<Component>();
  7.     
  8.     //增加一个叶子构件或树枝构件
  9.     public void add(Component component) {
  10.         this.componentArrayList.add(component);
  11.     }

  12.     //删除一个叶子构件或树枝构件
  13.     public void remove(Component component) {
  14.         this.componentArrayList.remove(component);
  15.     }

  16.     @Override
  17.     public void display(int depth) {
  18.         //输出树形结构
  19.         for(int i=0; i<depth; i++) {
  20.             System.out.print('-');
  21.         }
  22.         System.out.println(name);
  23.         
  24.         //下级遍历
  25.         for (Component component : componentArrayList) {
  26.             component.display(depth + 1);
  27.         }
  28.     }

  29. }
 3. Leaf
  叶子节点中没有了空实现,比较安全。

点击(此处)折叠或打开

  1. public class Leaf extends Component {

  2.     public Leaf(String name) {
  3.         super(name);
  4.     }

  5.     @Override
  6.     public void display(int depth) {
  7.         //输出树形结构的叶子节点
  8.         for(int i=0; i<depth; i++) {
  9.             System.out.print('-');
  10.         }
  11.         System.out.println(name);
  12.     }

  13. }


四、组合模式的应用
  1. 何时使用
想表达“部分-整体”层次结构(树形结构)时
希望用户忽略组合对象与单个对象的不同,用户将统一的使用组合结构中的所有对象
  2. 方法
树枝和叶子实现统一接口,树枝内部组合该接口
  3. 优点
高层模块调用简单。一棵树形机构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,高层模块不必关心自己处理的是单个对象还是整个组合结构。
节点自由增加
  4. 缺点
使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒转原则
  5. 使用场景
维护和展示部分-整体关系的场景(如树形菜单、文件和文件夹管理)
从一个整体中能够独立出部分模块或功能的场景

转自:https://www.cnblogs.com/adamjwh/p/9033547.html
https://www.cnblogs.com/Scott007/p/3472238.html


09-03 15:39