定义:
组合模式(Composite Pattern)又叫做部分-整体模式,它在树型结构(可以想象一下数据结构中的树)的问题中,模糊了简单元素和复杂元素的概念,客户端程序可以像处理简单元素一样来处理复杂元素,而使得客户端程序与复杂元素的内部结构进行解藕。
组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点。
如下图的工程目录结构就是一个树型结构:
它可以表示为下面的树型结构图:
由上面的结构图可以发现,根节点和树枝节点其实在本质上属于同一种数据类型,它们都可以作为容器使用;但是叶子节点与树枝节点其实在语义上是不属于同一种类型。但是在组合模式中,是把树枝节点和叶子节点看作属于同一种数据类型(这里是用统一接口定义),从而使得让它们具备一致行为。
所以在组合模式中,整个的树形结构中的对象就会都属于同一种类型,这样的好处就是不需要用户来辨别是树枝节点还是叶子节点,就可以直接进行操作,给用户的使用带来极大的便利。
组成部分:
通过上述对于组合模式的描述,可以总结出组合模式的几个组成部分:
1、Component: 组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件,不管是组合还是叶结点。
2、Leaf: 在组合中表示叶子结点对象,叶子结点没有子结点。
3、Composite: 容器对象,表示参加组合的有子对象的对象,定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加和删除等。
栗子
用上面文件夹的例子,首先声明一个文件夹相关的Component,FolderComponent:
public abstract class FolderComponent { private String name; public String getName() { return name; } public void setName(final String name) { this.name = name; } public FolderComponent() { } public FolderComponent(final String name) { this.name = name; } public abstract void add(FolderComponent component); public abstract void remove(FolderComponent component); public abstract void display(); } 复制代码
再声明一个叶子节点文件夹,定义为FileLeaf,并且继承上面的FolderComponent:
public class FileLeaf extends FolderComponent{ public FileLeaf(final String name) { super(name); } @Override public void add(FolderComponent component) { } @Override public void remove(FolderComponent component) { } @Override public void display() { System.out.println("叶子文件:" + this.getName()); } } 复制代码
最后声明一个容器对象,FolderComposit
public class FolderComposite extends FolderComponent { private final List<FolderComponent> components; public FolderComposite(final String name) { super(name); this.components = new ArrayList<FolderComponent>(); } public FolderComposite() { this.components = new ArrayList<FolderComponent>(); } @Override public void add(final FolderComponent component) { this.components.add(component); } @Override public void remove(final FolderComponent component) { this.components.remove(component); } @Override public void display() { System.out.println("文件夹组合容器的名字是" + this.getName()); for (final FolderComponent component : components) { System.out.println("文件夹组合容器的当前文件夹是" + component.getName()); } } } 复制代码
测试方法:
public class CompositePatternTest { public static void main(final String[] args) { final FolderComponent leaf = new FileLeaf("叶子文件"); leaf.display(); final FolderComponent folder = new FolderComposite("文件夹一"); folder.add(new FileLeaf("文件夹里面的文件二")); folder.add(new FileLeaf("文件夹里面的文件三")); folder.display(); } } 复制代码
运行结果如下:
通过上面的例子可以总结出组合模式的优点和缺点。
组合模式的优点
1、组合模式使得客户端代码可以一致地处理单个对象和组合对象,从而无须关心自己处理的是单个对象,还是组合对象,大大地简化了代码结构;
2、组合模式的使用使得更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,实现了设计模式原则的“开闭原则”;
组合模式的缺点
1、使用组合模式需要花较多地时间理清类之间的层次关系,所以设计较复杂;
2、组合模式理解和设计较为复杂,增加代码难度;
3、如果使用了组合模式就不容易限制容器中的构件;
4、组合模式中不容易用继承的方法来增加构件的新功能;
使用场景
组合模式适用于以下情况:
1、当想要表示对象的部分-整体层次结构
2、当想要用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
总结
使用组合模式方便解耦客户端与复杂元素的内部结构,从而使得客户端程序可以像处理简单元素一样来一致处理复杂元素,大大简化了代码。对于层次结构的系统对象,可以使用组合模式进行处理。