一、组合模式概述
组合模式通过一种巧妙的设计方案,使得用户可以一致性地处理整个树形结构或者树形结构的一部分,它描述了如何将容器对象和叶子对象进行递归组合,使得用户在使用时无需对它们进行区分,可以一致地对待容器对象和叶子对象,这就是组合模式的模式动机。
定义如下:
组合模式:组合多个对象形成树形结构以表示具有部分-整体关系的层次结构。组合模式可让客户端可以统一对待单个对象和组合对象。
二、组合模式结构与实现
2.1 组合模式结构
组合模式包含3个角色:
1,Component(抽象构件):它为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有的声明和实现。
2,Leaf(叶子构件):叶子结点没有子结点。
3,Composite(容器构件):容器结点包含子结点,其子结点可以是叶子结点,也可以是容器结点。它提供了一个集合用于存储子结点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子结点的业务方法。
2.2 组合模式实现
案例代码示例。
//对于抽象构件角色
/**
* 抽象构件类
*/
public abstract class AbstractControl {
public abstract void add(AbstractControl control);
public abstract void remote(AbstractControl control);
public abstract AbstractControl getChild(int i);
public abstract void show();
}
//叶子构件角色
/**
* 充当叶子构件类
*/
public class ButtonControl extends AbstractControl{
private String name;
public ButtonControl(String name) {
this.name = name;
}
@Override
public void add(AbstractControl control) {
System.out.println("对不起,不支持该方法");
}
@Override
public void remote(AbstractControl control) {
System.out.println("对不起,不支持该方法");
}
@Override
public AbstractControl getChild(int i) {
System.out.println("对不起,不支持该方法");
return null;
}
@Override
public void show() {
System.out.println("【"+this.name+"】模拟Button控件内容或业务方法");
}
}
/**
* 充当叶子构件类
*/
public class TextControl extends AbstractControl{
private String name;
public TextControl(String name) {
this.name = name;
}
@Override
public void add(AbstractControl control) {
System.out.println("对不起,不支持该方法");
}
@Override
public void remote(AbstractControl control) {
System.out.println("对不起,不支持该方法");
}
@Override
public AbstractControl getChild(int i) {
System.out.println("对不起,不支持该方法");
return null;
}
@Override
public void show() {
System.out.println("【"+this.name+"】模拟Text控件内容或业务方法");
}
}
//容器构件角色
/**
* 充当容器构件类
*/
public class Window extends AbstractControl {
private List<AbstractControl> controlList = new ArrayList<>();
private String name;
public Window(String name) {
this.name = name;
}
@Override
public void add(AbstractControl control) {
controlList.add(control);
}
@Override
public void remote(AbstractControl control) {
control.remote(control);
}
@Override
public AbstractControl getChild(int i) {
return controlList.get(i);
}
@Override
public void show() {
//比如要展示子类的所有内容或调用子类的所有业务方法,需要递归调用成员构件的方法
for (AbstractControl control : controlList) {
control.show();
}
}
}
/**
* 充当容器构件类
*/
public class Panel extends AbstractControl{
private List<AbstractControl> controlList = new ArrayList<>();
private String name;
public Panel(String name) {
this.name = name;
}
@Override
public void add(AbstractControl control) {
controlList.add(control);
}
@Override
public void remote(AbstractControl control) {
control.remote(control);
}
@Override
public AbstractControl getChild(int i) {
return controlList.get(i);
}
@Override
public void show() {
//比如要展示子类的所有内容或调用子类的所有业务方法,需要递归调用成员构件的方法
for (AbstractControl control : controlList) {
control.show();
}
}
}
//客户端
public class Client {
public static void main(String[] args) {
/**
* 案例需求描述:
* 要开发一个界面控件库,界面控件库分为两大类,一类是单元控件,例如按钮、文本框等,
* 一类是容器控件,例如窗体、中间面板等。
* 试用组合模式设计该组件控件库。
*/
//针对抽象构件编程
AbstractControl control1,control2,control3,control4,control5,control6,control7,control8,control9;
control1 = new Window("左边栏窗口");
control2 = new Window("右边栏窗口");
control3 = new Panel("右边栏面板");
control4 = new Panel("右边栏面板");
control5 = new ButtonControl("确认按钮");
control6 = new TextControl("标题");
control7 = new ButtonControl("取消按钮");
control8 = new TextControl("正文");
control9 = new ButtonControl("点赞按钮");
control1.add(control5);
control2.add(control6);
control3.add(control7);
control4.add(control8);
control1.add(control9);
//模拟点击某个界面控件
control2.show();
AbstractControl child = control1.getChild(0);
child.show();
AbstractControl child1 = control5.getChild(0);
}
}
三、组合模式优缺点和适用环境
3.1 组合模式的优点
- 可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制;
- 客户端可以一致地适用一个组合结构或某个单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码;
- 增加新的容器构件和叶子构件都很方便。
3.2 组合模式的缺点
- 增加新构件时很难对容器中的构件类型进行限制。
3.3 组合模式的适用环境
- 在具有整体和部分的层次结构中希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们;
- 在一个使用面向对象语言开发的系统中需要处理一个树形结构;
- 在一个系统中能够分理出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。
【参考文献】:
本文是根据刘伟的《Java设计模式》一书的学习笔记,仅供学习用途,勿做其他用途,请尊重知识产权。
【本文代码仓库】:https://gitee.com/xiongbomy/java-design-pattern.git