组合模式的概念
组合模式是一种结构型设计模式,它允许将对象组成树形结构,以表达“部分-整体” 结构,使得客户端可以使用统一的方式处理单个对象和组合对象。组合模式基于递归结构,将对象组织成一个树形结构,并且客户端对单个对象和组合对象的操作是一致的。组合模式可分为安全式组合模式和透明式组合模式两种,其中:
安全式组合模式中将 leaf 和 composite 的操作分开,即一些复杂的操作只对 composite 开发而不放到 component 中,通过这种方式,客户端只能调用 Composite 自身的操作,而不能调用 Leaf 的操作。
透明式组合模式消除了安全式组合模式的区分,使得 Leaf 和 Composite 具有相同的接口。透明组合模式中的所有子类都定义了 Component 接口中的所有接口,因此,客户端无论处理 Leaf 还是 Composite 时,都可以使用相同的接口,这样使得判断一个节点是组合节点还是叶子节点变得无所谓,这样带来的好处是客户端更加简单、直观。但是透明式组合模式的问题在于叶子节点并不支持添加子节点,这使得叶子节点的某些操作无意义。
组合模式的角色
Component: 组合对象的抽象基类,声明了组合对象的通用接口,定义了一个默认实现的子对象集合;
Leaf: 叶子对象,是组合的最基本对象,不包含任何子对象;
Composite: 复合对象,包含子组件,通常提供管理子对象的方法,比如 add、remove、get 等方法。
组合模式应用场景
需要表示对象的部分-整体层次结构;
需要客户端统一处理访问单个对象和组合对象;
需要对组合对象结构进行增加或者删除等操作时;
组合模式java举例
以上的概念也许难以理解组合模式,以下举一个java例子说明组合模式
首先,需要定义一个抽象的菜单组件类Component,它是抽象类或者接口,定义了菜单和子菜单的通用接口:
public abstract class Component {
private String name;
public Component(String name) {
this.name = name;
}
public String getName() {
return name;
}
public abstract void show(); // 显示菜单项
public abstract void add(Component component); // 添加子菜单或菜单项
public abstract void remove(Component component); // 删除子菜单或菜单项
public abstract Component getChild(int index); // 获取子菜单或菜单项
}
菜单或子菜单被定义为Composite,如下:
public class Menu extends Component {
private List<Component> menuList = new ArrayList<Component>();
public Menu(String name) {
super(name);
}
public void show() {
System.out.println("显示菜单项 " + getName());
}
public void add(Component component) {
System.out.println("不支持此操作");
}
public void remove(Component component) {
System.out.println("不支持此操作");
}
public Component getChild(int index) {
System.out.println("不支持此操作");
return null;
}
}
而菜单项被定义为Leaf,如下:
public class MenuItem extends Component {
public MenuItem(String name) {
super(name);
}
public void show() {
System.out.println("显示菜单项 " + getName());
}
public void add(Component component) {
System.out.println("不支持此操作");
}
public void remove(Component component) {
System.out.println("不支持此操作");
}
public Component getChild(int index) {
System.out.println("不支持此操作");
return null;
}
}
最后,我们可以通过如下代码来使用这些组件:
// 创建一个叶子菜单项
Component m1 = new MenuItem("菜单项1");
// 创建一个子菜单
Component m2 = new Menu("子菜单1");
// 给子菜单添加菜单项
Component m3 = new MenuItem("叶子菜单项1");
Component m4 = new MenuItem("叶子菜单项2");
((Menu) m2).add(m3);
((Menu) m2).add(m4);
// 创建一个顶级菜单
Component m5 = new Menu("顶级菜单1");
// 给顶级菜单添加子菜单和菜单项
((Menu) m5).add(m1);
((Menu) m5).add(m2);
// 显示菜单
m5.show();
该菜单系统使用了组合模式来实现,所有菜单项和子菜单都继承自抽象类Component,在使用时可以方便地进行扩展和组合。同时,使用组合模式还可以简化客户端的代码,客户端无需关心菜单项和子菜单的具体类型,只需通过统一的Component接口来访问它们即可。
UML类图如下:
组合模式的优缺点
组合模式的优点包括:
可以一致的处理单个对象和组合对象,无需关心具体的对象类型;
增加或删除组成部分非常容易,无需改变其它部分的代码;
组合模式为递归结构,非常符合自然界的组织方式。
组合模式的缺点包括:
在有些情况下,需要限制组合对象中的子组件,这时候 Composite 类的接口中需要添加一些额外的管理子对象的方法,会增加系统的复杂度;
组合模式的设计比较巧妙,但是可能会导致系统设计过于抽象化,使得程序难以读取和调试。