定义
将对象组合成树形结构以表示"部分-整体"的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。如电脑中文件和文件夹的结构。
结构
- 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中很多地方都使用到了组合模式(安全式)。
总结
优点
- 简化了客户端调用,客户端不需要区分叶子对象和组合对象。
- 可以很方便的增加新的叶子对象或组合对象,符合开闭原则。
- 可以组成一个复杂的树形结构,并且很容易控制。
缺点
- 很难限制组合对象中的组件类型,例如限制某个文件夹对象中只能包含文本文件,这种情况就很难实现。
本质
组合模式的本质是统一叶子对象和组合对象。正因为统一了这两种类型的对象,才能组成复杂的树形结构。
使用场景
- 在具有整体和部分的层次结构中,希望统一这两种结构的操作。
参考
大战设计模式【13】—— 组合模式
设计模式的征途—9.组合(Composite)模式
设计模式(十)——组合模式(HashMap源码解析)
组合模式(详解版)
《JAVA设计模式》之组合模式(Composite)
研磨设计模式-书籍