设计模式之组合模式

定义

将对象组合成树形结构以表示"部分-整体"的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。如电脑中文件和文件夹的结构。

结构

  • Component,抽象构件,为叶子节点对象和组合对象声明公共接口,并实现它们的默认行为。
  • Leaf,叶子节点对象,不包含其他的子节点对象。
  • Composite,组合对象,包含子节点,实现了抽象构件中定义的行为。

简单实现

抽象构件

public abstract class Component {

  protected String name;

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

  public void addChild(Component child) {
    throw new UnsupportedOperationException();
  }

  public void removeChild(Component child) {
    throw new UnsupportedOperationException();
  }

  public Component getChild(int index) {
    throw new UnsupportedOperationException();
  }

  public abstract void operation();
}

叶子节点对象

public class Leaf extends Component {

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

  @Override
  public void operation() {
    System.out.println(name + " operation");
  }

}

组合对象

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

public class Composite extends Component {

  private final List<Component> children = new ArrayList<>();

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

  public void addChild(Component child) {
    children.add(child);
  }

  public void removeChild(Component child) {
    children.remove(child);
  }

  public Component getChild(int index) {
    if (index < 0 || index >= children.size()) {
      return null;
    }
    return children.get(index);
  }

  @Override
  public void operation() {
    System.out.println(name + " operation");
    for (Component child : children) {
      child.operation();
    }
  }
}

客户端

public class Client {

  public static void main(String[] args) {
    //定义多个构件并组合成树形结构
    Component root = new Composite("C盘");
    Component dir1 = new Composite("目录1");
    Component dir2 = new Composite("目录2");
    Component dir11 = new Composite("目录11");
    Component file1 = new Leaf("文件1");
    Component file2 = new Leaf("文件2");
    dir1.addChild(dir11);
    dir11.addChild(file1);
    dir2.addChild(file2);
    root.addChild(dir1);
    root.addChild(dir2);
    root.operation();
    System.out.println("==========");
    root.removeChild(dir1);
    root.operation();
    System.out.println("==========");
    root.getChild(0).operation();
  }

}

组成的树形结构为

安全式和透明式

组合模式分为透明式的组合模式和安全式的组合模式。

  • 透明式,抽象构件声明了子类中的所有方法,所以客户端不需要区分叶子对象和组合对象,对客户端来说是透明的,
    但叶子对象本来没有addChild(),removeChild()方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。
  • 将管理子节点的方法转移到组合对象中,抽象构件和叶子节点中没有管理子节点的方法,这样就避免了上一种方式的安全性问题,
    但由于叶子节点对象和组合对象有不同的接口,客户端需要区分这两种对象,所以失去了透明性。

上面的示例实现就是透明式,下面实现一下安全式。

public abstract class Component {

  protected String name;

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

  public abstract void operation();
}

客户端

public class Client {

  public static void main(String[] args) {
    //定义多个构件并组合成树形结构
    Composite root = new Composite("C盘");
    Composite dir1 = new Composite("目录1");
    Composite dir2 = new Composite("目录2");
    Composite dir11 = new Composite("目录11");
    Component file1 = new Leaf("文件1");
    Component file2 = new Leaf("文件2");
    dir1.addChild(dir11);
    dir11.addChild(file1);
    dir2.addChild(file2);
    root.addChild(dir1);
    root.addChild(dir2);
    root.operation();
    System.out.println("==========");
    root.removeChild(dir1);
    root.operation();
    System.out.println("==========");
    root.getChild(0).operation();
  }

}

叶子节点对象和组合对象都没有改变。

组合模式在Spring的实现

Spring中的CompositeIterator和CompositeCacheManager

Spring中很多地方都使用到了组合模式(安全式)。

总结

优点

  1. 简化了客户端调用,客户端不需要区分叶子对象和组合对象。
  2. 可以很方便的增加新的叶子对象或组合对象,符合开闭原则。
  3. 可以组成一个复杂的树形结构,并且很容易控制。

缺点

  1. 很难限制组合对象中的组件类型,例如限制某个文件夹对象中只能包含文本文件,这种情况就很难实现。

本质

组合模式的本质是统一叶子对象和组合对象。正因为统一了这两种类型的对象,才能组成复杂的树形结构。

使用场景

  1. 在具有整体和部分的层次结构中,希望统一这两种结构的操作。

参考

大战设计模式【13】—— 组合模式
设计模式的征途—9.组合(Composite)模式
设计模式(十)——组合模式(HashMap源码解析)
组合模式(详解版)
《JAVA设计模式》之组合模式(Composite)
研磨设计模式-书籍

posted @ 2021-08-26 20:36  strongmore  阅读(69)  评论(0编辑  收藏  举报