一、定义
组合模式(Composite Pattern):结构型模式之一,组合多个对象形成树形结构以表示“整体-部分”的关系的层次结构。组合模式对叶子节点和容器节点的处理具有一致性,又称为整体-部分模式。
二、UML类图
三、角色职责
- 抽象构件(Component):叶子构件与容器构件共同继承的父类或者是共同实现的接口,该角色中包含所有子类共有方法的声明和实现,在抽象构件中定义了管理子构件的方法,新增构件、删除构件、获取构件。
- 叶子构件(Leaf):表示叶子节点,没有子节点,对于继承父类的管理子节点的方法以抛出异常的方式处理。
- 容器构件(Composite):表示容器节点,包含子节点,子节点可以是容器节点也可以是叶子节点,其提供一个集合来对子节点进行维护,以迭代的方式对子节点进行处理。
四、代码实现
前言:现在疫情形势异常严峻,那各个省市的疾控中心是怎样收到的通知,又是怎样下发的呢?首先是中央层面的中国疾控中心,这是所有疾控中心的上级,他会将命令下发给省级的疾控中心,然后省级的疾控中心收到通知后,又会下发给市级的疾控中心,这样依次下发到区、县、镇、村…如果应用到代码层面,这就是组合模式。
抽象构件 Component
public interface Unit {
void add(Unit unit);
void remove(Unit unit);
Unit getChild(int i);
void sendMessage(String message);
}
容器构件 Composite
@Data
@NoArgsConstructor
public class Subsidiary implements Unit {
private List<Unit> units = new ArrayList<>();
private String name;
public Subsidiary(String name) {
this.name = name;
}
@Override
public void add(Unit unit) {
units.add(unit);
}
@Override
public void remove(Unit unit) {
units.remove(unit);
}
@Override
public Unit getChild(int i) {
return units.get(i);
}
@Override
public void sendMessage(String message) {
System.out.println(name + "收到通知");
System.out.println(name + "下发通知");
units.forEach(child -> child.sendMessage(message));
}
}
疾控中心 (叶子构件 Leaf)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CDC implements Unit {
private String name;
@Override
public void add(Unit unit) {
System.out.println(name + "没有子节点");
}
@Override
public void remove(Unit unit) {
System.out.println(name + "没有子节点");
}
@Override
public Unit getChild(int i) {
System.out.println(name + "没有子节点");
return null;
}
@Override
public void sendMessage(String message) {
System.out.println(name + "收到通知");
}
}
测试类
public class CompositePattern {
public static void main(String[] args) {
// 中国疾控中心
Unit head = new Subsidiary("中国疾控中心");
// 江苏疾控中心
Unit jsCDC = new Subsidiary("江苏疾控中心");
head.add(jsCDC);
// 南京疾控中心
Unit njCDC = new CDC("南京疾控中心");
jsCDC.add(njCDC);
// 徐州疾控中心
Unit xzCDC = new CDC("徐州疾控中心");
jsCDC.add(xzCDC);
// 广东疾控中心
Unit gdCDC = new Subsidiary("广东疾控中心");
head.add(gdCDC);
// 广州疾控中心
Unit gzCDC = new CDC("广州疾控中心");
gdCDC.add(gzCDC);
// 东莞疾控中心
Unit dgCDC = new CDC("东莞疾控中心");
gdCDC.add(dgCDC);
head.sendMessage("全民戴口罩");
}
}
输出结果
中国疾控中心收到通知
中国疾控中心下发通知
江苏疾控中心收到通知
江苏疾控中心下发通知
南京疾控中心收到通知
徐州疾控中心收到通知
广东疾控中心收到通知
广东疾控中心下发通知
广州疾控中心收到通知
东莞疾控中心收到通知
五、源码分析
在Mybatis中,就适用到了组合模式。
SqlNode接口有很多的实现类,他们就一级节点。
我们随便点进一个一级节点TrimSqlNode,可以看到二级节点WhereSqlNode。
Mybatis在处理动态SQL节点时,应用到了组合设计模式,MyBatis会将映射文件中定义的静态SQL节点、文本节点等解析成对应的SqlNode实现,形成树形结构。
六、优缺点分析
优点
- 节点自由增加。使用了组合模式后,如果想增加一个树枝节点、树叶节点十分简单,只要找到它的父节点就成,非常容易扩展,符合开闭原则,有利于后期维护。
缺点
- 树枝树叶直接使用了实现类,这在面向接口编程上是很不恰当的,与依赖倒置原则冲突,限制了接口的影响范围。
七、适用场景
在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加—些新的类型时,可以使用组合模式。
八、总结
组合模式使用面向对象的思想来实现树形结构的构建与处理,描述了如何将容器对象和叶子对象进行递归组合,实现简单,灵活性好。由于在软件开发中存在大量的树形结构,因此组合模式是一种使用频率较高的结构型设计模式。