复合模式是一种分区设计模式。描述了一组对象,它们的处理方式与相同类型对象的单个实例相同。复合的目的是将对象“组合”成树结构以表示部分-整体层次结构。
实现复合模式,可以让客户端统一处理单个对象和组合。
哪些问题可以用复合设计模式解决?
* 需要表示成部分-整体层次结构,以便客户端可以统一处理部分和整体对象;
* 部分-整体层次结构需要表示成树结构。
注:定义(1)part 对象和(2)whole 充当 part 对象容器的对象时,客户端需要区别对待它们,使得客户端代码复杂化。
复合设计模式描述了什么解决方案?
* Component 为部分 Leaf 对象和整体 Composite 对象定义统一接口;
* 单个 Leaf 对象 Component 直接实现接口,Composite 对象将请求转发给它们的子组件。
客户端可以通过 Component 接口统一对待 Leaf 和 Composite 对象。使得客户端类更易于实现、更改、测试、重用。
动机:
处理树结构时,通常需要区分叶子结点和分支,使得代码复杂且易出错。解决方案是一个允许统一处理复杂和原始对象的接口。
面向对象的编程中,组合是一个对象,被设计为一个或多个相似对象的组合,所有对象都表现出相似的功能。这被称为对象之间的“has-a”关系。
何时使用:
当客户端忽略对象组合和单个对象之间的差异时,应该使用组合。如果程序员发现他们以相同的方式使用多个对象,并且经常有几乎相同的代码来处理每个对象,那么复合是一个不错的选择。在这种情况下,将原始的和复合的视为同构,并不复杂。
示例:UML 类和对象图
定义与子相关的操作
存在 2 种设计变体,用于定义和实现与子组件相关的操作,例如在容器中添加/删除/访问子组件
* 统一性设计:在 Component 接口中定义与子相关的操作,使得客户端能统一对待 Leaf 、Composite 对象。但是类型安全丢失了,因为客户端可以对 Leaf 对象执行与子相关的操作。
* 类型安全设计:仅在 Composite 类中定义与子相关的操作。
注:复合设计模式强调一致性而不是类型安全。
UML 类图
Component
* 所有组件的抽象,包括复合组件
* 声明组合中对象的接口
* (可选)定义一个用于访问递归结构中组件父级的接口,并在适当时实现它
Leaf
* 表示组合中的叶对象
* 实现所有组件方法
Composite
* 表示一个复合组件(有子组件的组件)
* 实现操作孩子的方法
* 实现所有Component方法,通常是通过将它们委托给它的孩子
Java 代码示例
import java.util.List; import java.util.ArrayList; /** "Component" */ interface Graphic { //Prints the graphic. public void print(); } /** "Composite" */ class CompositeGraphic implements Graphic { //Collection of child graphics. private final List<Graphic> childGraphics = new ArrayList<>(); //Adds the graphic to the composition. public void add(Graphic graphic) { childGraphics.add(graphic); } //Prints the graphic. @Override public void print() { for (Graphic graphic : childGraphics) { graphic.print(); //Delegation } } } /** "Leaf" */ class Ellipse implements Graphic { //Prints the graphic. @Override public void print() { System.out.println("Ellipse"); } } /** Client */ class CompositeDemo { public static void main(String[] args) { //Initialize four ellipses Ellipse ellipse1 = new Ellipse(); Ellipse ellipse2 = new Ellipse(); Ellipse ellipse3 = new Ellipse(); Ellipse ellipse4 = new Ellipse(); //Creates two composites containing the ellipses CompositeGraphic compositGraphic2 = new CompositeGraphic(); compositGraphic2.add(ellipse1); compositGraphic2.add(ellipse2); compositGraphic2.add(ellipse3); CompositeGraphic compositGraphic3 = new CompositeGraphic(); compositGraphic3.add(ellipse4); //Create another graphics that contains two graphics CompositeGraphic compositGraphic = new CompositeGraphic(); compositGraphic.add(compositGraphic2); compositGraphic.add(compositGraphic3); //Prints the complete graphic (Four times the string "Ellipse"). compositGraphic.print(); } }