引言
上一节说了中介者模式,这话咱们说说组合模式。这个模式主要是用来表示具有“整体—部分”关系的层次结构。
示例地址
类图
image定义
将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
使用场景
表示对象的部分-整体层次结构时。
从一个整体中能够独立出部分模块或功能的场景。
背景故事
我们去服装市场,看看服装的分类,服装下面有男装、女装。男装下面有包含短裤、衬衫,女装下面包含裙子、小脚裤。按照我们一半的写法,可以这样写。
一般写法
1. 子节点
/**
* 子节点
*
* @author 512573717@qq.com
* @created 2018/8/2 上午11:43.
*/
public class ChildNode {
private String nodeName;
public ChildNode(String nodeName) {
this.nodeName = nodeName;
}
public void getNickname(String pre) {
System.out.println(pre + "-" + nodeName);
}
}
2. 父节点
/**
* 父节点
*
* @author 512573717@qq.com
* @created 2018/8/2 下午2:00.
*/
public class ParentNode {
private List<ChildNode> mLeafList = new ArrayList<>();
private List<ParentNode> mParentList = new ArrayList<>();
private String nodeName;
public ParentNode(String nodeName) {
this.nodeName = nodeName;
}
public void addParent(ParentNode parent) {
mParentList.add(parent);
}
public void addLeaf(ChildNode leaf) {
mLeafList.add(leaf);
}
public void getNickname(String pre) {
System.out.println(pre + nodeName);
//然后添加一个空格,表示向后缩进一个空格,输出自己包含的叶子对象
pre += " ";
for (ChildNode leaf : mLeafList) {
leaf.getNickname(pre);
}
//输出当前对象的子对象了
for (ParentNode c : mParentList) {
//递归输出每个子对象
c.getNickname(pre);
}
}
}
3. Client
//定义所有的组合对象
ParentNode root = new ParentNode("服装");
ParentNode c1 = new ParentNode("男装");
ParentNode c2 = new ParentNode("女装");
//定义所有的叶子对象
ChildNode leaf1 = new ChildNode("衬衣");
ChildNode leaf2 = new ChildNode("夹克");
ChildNode leaf3 = new ChildNode("裙子");
ChildNode leaf4 = new ChildNode("套装");
//按照树的结构来组合组合对象和叶子对象
root.addParent(c1);
root.addParent(c2);
c1.addLeaf(leaf1);
c1.addLeaf(leaf2);
c2.addLeaf(leaf3);
c2.addLeaf(leaf4);
//调用根对象的输出功能来输出整棵树
root.getNickname("");
4. Result
服装
男装
-衬衣
-夹克
女装
-裙子
-套装
5. 存在缺点
需要知道那个是子节点,那个是父节点。然后按照对应的节点去添加。区别对待组合对象和叶子对象,不仅让程序变得复杂,还对功能的扩展也带来不便。
组合模式实现
1. 抽象节点类
/**
* 节点抽象类
*
* @author 512573717@qq.com
* @created 2018/8/2 下午2:17.
*/
public abstract class Node {
//节点名字
protected String nodeName;
public Node(String nodeName) {
this.nodeName = nodeName;
}
public void setNodeName(String nodeName) {
this.nodeName = nodeName;
}
// 打印节点名字
public abstract void getNodeName(String prefix);
//添加节点
public void addNode(Node node) {
}
//删除节点
public void removeNode(Node node) {
}
//查找节点
public void getIndexNode(int index) {
}
}
2. 定义父节点
/**
* 父节点
*
* @author 512573717@qq.com
* @created 2018/8/2 下午2:43.
*/
public class ParentNode extends Node {
private List<Node> mParents = null;
public ParentNode(String nodeName) {
super(nodeName);
}
@Override
public void getNodeName(String prefix) {
System.out.println(prefix+nodeName);
if (this.mParents != null) {
prefix +=" ";
for (Node c : mParents) {
//递归输出每个子对象
c.getNodeName(prefix);
}
}
}
@Override
public void addNode(Node parent) {
if (mParents == null) {
mParents = new ArrayList<Node>();
}
mParents.add(parent);
}
}
3. 定义子节点
/**
* 子节点
*
* @author 512573717@qq.com
* @created 2018/8/2 下午2:25.
*/
public class ChildNode extends Node {
public ChildNode(String nodeName) {
super(nodeName);
}
@Override
public void getNodeName(String prefix) {
System.out.println(prefix+"-"+ nodeName);
}
}
4. Client
Node root = new ParentNode("服装");
Node c1 = new ParentNode("男装");
Node c2 = new ParentNode("女装");
//定义所有的叶子对象
Node leaf1 = new ChildNode("衬衣");
Node leaf2 = new ChildNode("夹克");
Node leaf3 = new ChildNode("裙子");
Node leaf4 = new ChildNode("套装");
//按照树的结构来组合组合对象和叶子对象
root.addNode(c1);
root.addNode(c2);
c1.addNode(leaf1);
c1.addNode(leaf2);
c2.addNode(leaf3);
c2.addNode(leaf4);
//调用根对象的输出功能来输出整棵树
root.getNodeName("");
组合模式的2种实现方式
安全性实现
如果把管理子组件的操作定义在ParentNode中,那么客户在使用叶子对象的时候,就不会发生使用添加子组件或是删除子组件的操作了,因为压根就没有这样的功能,这种实现方式是安全的。
透明性实现
如果把管理子组件的操作定义在Node中,那么客户端只需要面对Node,而无需关心具体的组件类型,这种实现方式就是透明性的实现。上面的示例就是透明性的。
总结
通过使用组合模式,把一个“部分-整体”的层次结构表示成了对象树的结构,这样一来,客户端就无需再区分操作的是组合对象还是叶子对象了,对于客户端而言,操作的都是组件对象。
网友评论