友情支持

如果您觉得这个笔记对您有所帮助,看在D瓜哥码这么多字的辛苦上,请友情支持一下,D瓜哥感激不尽,😜

支付宝

微信

有些打赏的朋友希望可以加个好友,欢迎关注D 瓜哥的微信公众号,这样就可以通过公众号的回复直接给我发信息。

wx jikerizhi

公众号的微信号是: jikerizhi因为众所周知的原因,有时图片加载不出来。 如果图片加载不出来可以直接通过搜索微信号来查找我的公众号。

11. 组合模式

11.1. 定义

根据 GoF 的著名著作 《设计模式》,组合模式的定义如下:

组合模式(Composite)

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

— Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides
《设计模式》

11.2. 类图

Diagram

组合模式 注: Netty 中 CompositeByteBuf 就是组合模式的实现。

Component为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component的子部件。

Leaf在组合中表示叶节点对象,叶节点没有子节点。

Composite定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关的操作,比如增加Add和删除Remove。

客户端代码,能通过Component接口操作组合部件的对象。

透明方式,也就是说在Component中声明所有用来管理子对象的方法,其中包括Add、Remove等。这样实现Component接口的所有子类都具备了Add和Remove。这样做的好处就是叶节点和枝节点对于外界没有区别,它们具备完全一致的行为接口。但问题也很明显,因为Leaf类本身不具备Add()、Remove()方法的功能,所以实现它是没有意义的。

安全方式,也就是在Component接口中不去声明Add和Remove方法,那么子类的Leaf也就不需要去实现它,而是在Composite声明所有用来管理子类对象的方法,这样做就不会出现刚才提到的问题,不过由于不够透明,所以树叶和树枝类将不具有相同的接口,客户端的调用需要做相应的判断,带来了不便。

当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了

组合模式这样就定义了包含人力资源部和财务部这些基本对象和分公司、办事处等组合对象的类层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象了。

用户是不用关心到底是处理一个叶节点还是处理一个组合组件,也就用不着为定义组合而写一些选择判断语句了。

组合模式让客户可以一致地使用组合结构和单个对象。

代码 72. composite/Client.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.diguage.didp.composite;

/**
 * Client 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-19 17:53:34
 */
public class Client {
  public static void main(String[] args) {
    Composite root = new Composite("root");
    root.add(new Leaf("Leaf A"));
    root.add(new Leaf("Leaf B"));

    Composite comp = new Composite("Composite X");
    comp.add(new Leaf("Leaf XA"));
    comp.add(new Leaf("Leaf XB"));

    root.add(comp);

    Composite comp2 = new Composite("Composite XY");
    comp2.add(new Leaf("Leaf XYA"));
    comp2.add(new Leaf("Leaf XYB"));
    comp.add(comp2);

    root.add(new Leaf("Leaf C"));

    Leaf leaf = new Leaf("Leaf D");
    root.add(leaf);
    root.remove(leaf);

    root.display(1);
  }
}
代码 73. composite/Component.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.diguage.didp.composite;

/**
 * Component 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-23 09:09:48
 */
public abstract class Component {
  protected String name;

  public Component(String name) {
    this.name = name;
  }

  public abstract void add(Component component);

  public abstract void remove(Component component);

  public abstract void display(int depth);
}
代码 74. composite/Composite.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.diguage.didp.composite;

import java.util.ArrayList;
import java.util.List;

/**
 * Composite 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-23 09:09:48
 */
public class Composite extends Component {
  private List<Component> children = new ArrayList<>();

  public Composite(String name) {
    super(name);
  }

  @Override
  public void add(Component component) {
    children.add(component);
  }

  @Override
  public void remove(Component component) {
    children.remove(component);
  }

  @Override
  public void display(int depth) {
    StringBuilder sb = new StringBuilder(depth + name.length());
    for (int i = 0; i < depth; i++) {
      sb.append("-");
    }
    System.out.println(sb.append(name).toString());

    children.forEach(s -> s.display(depth + 2));
  }
}
代码 75. composite/Leaf.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.diguage.didp.composite;

/**
 * Leaf 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-23 09:09:48
 */
public class Leaf extends Component {
  public Leaf(String name) {
    super(name);
  }

  @Override
  public void add(Component component) {
    throw new UnsupportedOperationException("不支持向叶子中添加元素!");
  }

  @Override
  public void remove(Component component) {
    throw new UnsupportedOperationException("不支持向叶子中删除元素!");
  }

  @Override
  public void display(int depth) {
    StringBuilder sb = new StringBuilder(depth + name.length());
    for (int i = 0; i < depth; i++) {
      sb.append("-");
    }
    System.out.println(sb.append(name).toString());
  }
}

11.3. 常见示例

  1. java.util.Map.putAll(Map<? extends K, ? extends V> m)

  2. java.util.List.addAll(*)

  3. java.util.Set.addAll(Collection<? extends E> c)

  4. Netty 中对 Buff 的管理似乎也是这个模式。