场景
-
客户订单的目的地问题
-
每个地方行政区划有三级至五级不等
-
组织这个一样目录,供客户选择
-
如果每级区划使用一个类,需要五个类,太复杂
-
- 于是我们可以把一组相似的对象当作一个单一的对象,为的是减少数据类型。
组合模式
定义
将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。
结构
-
抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。
-
树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。
-
树枝构件(Composite)角色:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
UML类图
代码实现
实现
抽象构件
/**
* 抽象构件
*/
public abstract class ComponentNode {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ComponentNode(String name) {
this.name = name;
}
public abstract List<ComponentNode> getChildren();
public abstract void add(ComponentNode node);
public abstract void remove(ComponentNode node);
}
树叶构件
public class LeafNode extends ComponentNode{
public LeafNode(String name) {
super(name);
}
@Override
public List<ComponentNode> getChildren() {
return null;
}
@Override
public void add(ComponentNode node) { }
@Override
public void remove(ComponentNode node) { }
}
树枝构件
public class CompositeNode extends ComponentNode {
private List<ComponentNode> children = new ArrayList<>();
public CompositeNode(String name) {
super(name);
}
@Override
public List<ComponentNode> getChildren() {
return children;
}
@Override
public void add(ComponentNode node) {
children.add(node);
}
@Override
public void remove(ComponentNode node) {
children.remove(node);
}
}
客户端调用
public class TestClient {
public static void main(String[] args) {
//根目录
ComponentNode root = new CompositeNode("目的地");
//第一级
ComponentNode bj = new CompositeNode("北京");
ComponentNode sh = new CompositeNode("上海");
ComponentNode tj = new CompositeNode("天津");
root.add(bj);
root.add(sh);
root.add(tj);
//第二级,属于北京
ComponentNode cyq = new CompositeNode("朝阳区");
ComponentNode hdq = new CompositeNode("海淀区");
ComponentNode xcq = new CompositeNode("西城区");
bj.add(cyq);
bj.add(hdq);
bj.add(xcq);
//第三级,属于朝阳区
ComponentNode cwjd = new CompositeNode("朝外街道");
ComponentNode slt = new CompositeNode("三里屯");
cyq.add(cwjd);
cyq.add(slt);
//最后一级,没有子节点
LeafNode leafNode = new LeafNode("三里屯卤煮店");
slt.add(leafNode);
System.out.println(JSON.toJSON(root));
}
}
结果
{"children":[{"children":[{"children":[{"children":[],"name":"朝外街道"},{"children":[{"name":"三里屯卤煮店"}],"name":"三里屯"}],"name":"朝阳区"},{"children":[],"name":"海淀区"},{"children":[],"name":"西城区"}],"name":"北京"},{"children":[],"name":"上海"},{"children":[],"name":"天津"}],"name":"目的地"}
优缺点
优点
- 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码
- 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”
缺点
- 设计较复杂,客户端需要花更多时间理清类之间的层次关系
- 不容易限制容器中的构件
- 不容易用继承的方法来增加构件的新功能
组合模式的本质
组合模式的本质:统一叶子对象和组合对象
组合模式通过吧叶子对象当成特殊的组合对象看待,叶子对象和组合对象全部当成Component对象,统一了叶子对象和组合对象
使用场景
- 在需要表示一个对象整体与部分的层次结构的场合。
- 要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。