文章目录
组合模式(Composite Pattern)
组合模式的定义
- 组合模式(Composite Pattern)
- 组合模式的定义
- 组合模式的优点
- 组合模式的缺点
- 组合模式的结构
- 组合模式的实现
- (1) 透明方式
- (2) 安全方式
- (3) 其他具现使用
- 组合模式的使用场景
将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
组合模式的优点- 高层模块调用简单 树形结构的节点都是Component,使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
- 节点自由的增加 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;
- 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
- 不容易限制容器中的构件;
- 不容易用继承的方法来增加构件的新功能;
- 在使用树枝和树叶时,使用了实现类,破坏了依赖倒置原则
组合模式包含以下主要角色。
- 抽象构件(Component)角色:定义了参见组合的对象的共有方法和属性,可以定义一些默认的行为和属性。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。
- 叶子构件(Leaf)角色:叶子对象,其下在没有其他分支,用于继承或实现抽象构件。
- 树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。主要是组合树枝节点和叶子节点形成树形结构,它的主要作用是存储和管理子部件。
组合模式分为透明式的组合模式和安全式的组合模式。
(1) 透明方式在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。
public interface Component {
public void add(Component c);
public void remove(Component c);
public Component getChild(int i);
public void operation();
}
public class Composite implements Component {
private ArrayList children = new ArrayList();
@Override
public void add(Component c) {
children.add(c);
}
@Override
public void remove(Component c) {
children.remove(c);
}
@Override
public Component getChild(int i) {
return children.get(i);
}
@Override
public void operation() {
for (Object obj : children) {
((Component) obj).operation();
}
}
}
public class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void add(Component c) {
}
@Override
public void remove(Component c) {
}
@Override
public Component getChild(int i) {
return null;
}
@Override
public void operation() {
System.out.println("树叶" + name + ":被访问!");
}
}
public static void main(String[] args) {
Component c0 = new Composite();
Component c1 = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
Component leaf3 = new Leaf("3");
c0.add(leaf1);
c0.add(c1);
c1.add(leaf2);
c1.add(leaf3);
c0.operation();
}
(2) 安全方式
在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。
public interface Component {
public void operation();
}
public class Composite implements Component {
private ArrayList children = new ArrayList();
public void add(Component c) {
this.children.add(c);
}
public void remove(Component c) {
this.children.remove(c);
}
public Component getChild(int i) {
return this.children.get(i);
}
@Override
public void operation() {
for (Object obj : children) {
((Component) obj).operation();
}
}
}
public class Leaf implements Component {
private final String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void operation() {
System.out.println("树叶" + name + ":被访问!");
}
}
public static void main(String[] args) {
Composite c0 = new Composite();
Composite c1 = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
Component leaf3 = new Leaf("3");
c0.add(leaf1);
c0.add(c1);
c1.add(leaf2);
c1.add(leaf3);
c0.operation();
}
(3) 其他具现使用
/**
* 树根节点
*/
public class TreeRoot {
/**
* 规则树ID
*/
private Long treeId;
/**
* 规则树根ID
*/
private Long treeRootNodeId;
/**
* 规则树名称
*/
private String treeName;
public Long getTreeId() {
return treeId;
}
public void setTreeId(Long treeId) {
this.treeId = treeId;
}
public Long getTreeRootNodeId() {
return treeRootNodeId;
}
public void setTreeRootNodeId(Long treeRootNodeId) {
this.treeRootNodeId = treeRootNodeId;
}
public String getTreeName() {
return treeName;
}
public void setTreeName(String treeName) {
this.treeName = treeName;
}
}
/**
* 树的连接线
*/
public class TreeNodeLink {
/**
* 节点From
*/
private Long nodeIdFrom;
/**
* 节点To
*/
private Long nodeIdTo;
/**
* 限定类型;1:=;2:>;3:=;5 子节点
*/
private Map treeNodeMap;
public TreeRich(TreeRoot treeRoot, Map treeNodeMap) {
this.treeRoot = treeRoot;
this.treeNodeMap = treeNodeMap;
}
public TreeRoot getTreeRoot() {
return treeRoot;
}
public void setTreeRoot(TreeRoot treeRoot) {
this.treeRoot = treeRoot;
}
public Map getTreeNodeMap() {
return treeNodeMap;
}
public void setTreeNodeMap(Map treeNodeMap) {
this.treeNodeMap = treeNodeMap;
}
}
/**
* 决策结果
*/
public class EngineResult {
/**
* 执行结果
*/
private boolean isSuccess;
/**
* 用户ID
*/
private String userId;
/**
* 规则树ID
*/
private Long treeId;
/**
* 果实节点ID
*/
private Long nodeId;
/**
* 果实节点值
*/
private String nodeValue;
public EngineResult() {
}
public EngineResult(boolean isSuccess) {
this.isSuccess = isSuccess;
}
public EngineResult(String userId, Long treeId, Long nodeId, String nodeValue) {
this.isSuccess = true;
this.userId = userId;
this.treeId = treeId;
this.nodeId = nodeId;
this.nodeValue = nodeValue;
}
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean success) {
isSuccess = success;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public Long getTreeId() {
return treeId;
}
public void setTreeId(Long treeId) {
this.treeId = treeId;
}
public Long getNodeId() {
return nodeId;
}
public void setNodeId(Long nodeId) {
this.nodeId = nodeId;
}
public String getNodeValue() {
return nodeValue;
}
public void setNodeValue(String nodeValue) {
this.nodeValue = nodeValue;
}
}
public interface LogicFilter {
/**
* 逻辑决策器
*
* @param matterValue 决策值
* @param treeNodeLineInfoList 决策节点树
* @return 下一个节点Id
*/
public Long filter(String matterValue, List treeNodeLineInfoList);
/**
* 获取决策值
*
* @param decisionMatter 决策条件
* @return 决策值
*/
public String matterValue(Long treeId, String userId, Map decisionMatter);
}
public abstract class BaseLogic implements LogicFilter {
@Override
public Long filter(String matterValue, List treeNodeLinkList) {
for (TreeNodeLink nodeLine : treeNodeLinkList) {
if (decisionLogic(matterValue, nodeLine)) {
return nodeLine.getNodeIdTo();
}
}
return 0L;
}
@Override
public abstract String matterValue(Long treeId, String userId, Map decisionMatter);
private boolean decisionLogic(String matterValue, TreeNodeLink nodeLink) {
switch (nodeLink.getRuleLimitType()) {
case 1:
return matterValue.equals(nodeLink.getRuleLimitValue());
case 2:
return Double.parseDouble(matterValue) > Double.parseDouble(nodeLink.getRuleLimitValue());
case 3:
return Double.parseDouble(matterValue) 11
TreeNodeLink treeNodeLink_11 = new TreeNodeLink();
treeNodeLink_11.setNodeIdFrom(1L);
treeNodeLink_11.setNodeIdTo(11L);
treeNodeLink_11.setRuleLimitType(1);
treeNodeLink_11.setRuleLimitValue("man");
// 链接:1->12
TreeNodeLink treeNodeLink_12 = new TreeNodeLink();
treeNodeLink_12.setNodeIdTo(1L);
treeNodeLink_12.setNodeIdTo(12L);
treeNodeLink_12.setRuleLimitType(1);
treeNodeLink_12.setRuleLimitValue("woman");
List treeNodeLinkList_1 = new ArrayList();
treeNodeLinkList_1.add(treeNodeLink_11);
treeNodeLinkList_1.add(treeNodeLink_12);
treeNode_01.setTreeNodeLinkList(treeNodeLinkList_1);
// 节点:11
TreeNode treeNode_11 = new TreeNode();
treeNode_11.setTreeId(10001L);
treeNode_11.setTreeNodeId(11L);
treeNode_11.setNodeType(1);
treeNode_11.setNodeValue(null);
treeNode_11.setRuleKey("userAge");
treeNode_11.setRuleDesc("用户年龄");
// 链接:11->111
TreeNodeLink treeNodeLink_111 = new TreeNodeLink();
treeNodeLink_111.setNodeIdFrom(11L);
treeNodeLink_111.setNodeIdTo(111L);
treeNodeLink_111.setRuleLimitType(3);
treeNodeLink_111.setRuleLimitValue("25");
// 链接:11->112
TreeNodeLink treeNodeLink_112 = new TreeNodeLink();
treeNodeLink_112.setNodeIdFrom(11L);
treeNodeLink_112.setNodeIdTo(112L);
treeNodeLink_112.setRuleLimitType(5);
treeNodeLink_112.setRuleLimitValue("25");
List treeNodeLinkList_11 = new ArrayList();
treeNodeLinkList_11.add(treeNodeLink_111);
treeNodeLinkList_11.add(treeNodeLink_112);
treeNode_11.setTreeNodeLinkList(treeNodeLinkList_11);
// 节点:12
TreeNode treeNode_12 = new TreeNode();
treeNode_12.setTreeId(10001L);
treeNode_12.setTreeNodeId(12L);
treeNode_12.setNodeType(1);
treeNode_12.setNodeValue(null);
treeNode_12.setRuleKey("userAge");
treeNode_12.setRuleDesc("用户年龄");
// 链接:12->121
TreeNodeLink treeNodeLink_121 = new TreeNodeLink();
treeNodeLink_121.setNodeIdFrom(12L);
treeNodeLink_121.setNodeIdTo(121L);
treeNodeLink_121.setRuleLimitType(3);
treeNodeLink_121.setRuleLimitValue("25");
// 链接:12->122
TreeNodeLink treeNodeLink_122 = new TreeNodeLink();
treeNodeLink_122.setNodeIdFrom(12L);
treeNodeLink_122.setNodeIdTo(122L);
treeNodeLink_122.setRuleLimitType(5);
treeNodeLink_122.setRuleLimitValue("25");
List treeNodeLinkList_12 = new ArrayList();
treeNodeLinkList_12.add(treeNodeLink_121);
treeNodeLinkList_12.add(treeNodeLink_122);
treeNode_12.setTreeNodeLinkList(treeNodeLinkList_12);
// 节点:111
TreeNode treeNode_111 = new TreeNode();
treeNode_111.setTreeId(10001L);
treeNode_111.setTreeNodeId(111L);
treeNode_111.setNodeType(2);
treeNode_111.setNodeValue("果实A");
// 节点:112
TreeNode treeNode_112 = new TreeNode();
treeNode_112.setTreeId(10001L);
treeNode_112.setTreeNodeId(112L);
treeNode_112.setNodeType(2);
treeNode_112.setNodeValue("果实B");
// 节点:121
TreeNode treeNode_121 = new TreeNode();
treeNode_121.setTreeId(10001L);
treeNode_121.setTreeNodeId(121L);
treeNode_121.setNodeType(2);
treeNode_121.setNodeValue("果实C");
// 节点:122
TreeNode treeNode_122 = new TreeNode();
treeNode_122.setTreeId(10001L);
treeNode_122.setTreeNodeId(122L);
treeNode_122.setNodeType(2);
treeNode_122.setNodeValue("果实D");
// 树根
TreeRoot treeRoot = new TreeRoot();
treeRoot.setTreeId(10001L);
treeRoot.setTreeRootNodeId(1L);
treeRoot.setTreeName("规则决策树");
Map treeNodeMap = new HashMap();
treeNodeMap.put(1L, treeNode_01);
treeNodeMap.put(11L, treeNode_11);
treeNodeMap.put(12L, treeNode_12);
treeNodeMap.put(111L, treeNode_111);
treeNodeMap.put(112L, treeNode_112);
treeNodeMap.put(121L, treeNode_121);
treeNodeMap.put(122L, treeNode_122);
treeRich = new TreeRich(treeRoot, treeNodeMap);
System.out.println("决策树组合结构信息:\r\n" + JSON.toJSONString(treeRich));
IEngine treeEngineHandle = new TreeEngineHandle();
Map decisionMatter = new HashMap();
decisionMatter.put("gender", "man");
decisionMatter.put("age", "29");
EngineResult result = treeEngineHandle.process(10001L, "Oli09pLkdjh", treeRich, decisionMatter);
System.out.println("测试结果:{" + JSON.toJSONString(result) + "}");
}
组合模式的使用场景
当无法或不想直接引用某个对象或访问某个对象存在困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
- 维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理。
- 从一个整体中能够独立出部分模块或功能的场景。
- 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
- 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
- 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
- 智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。
- 延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate中就存在属性的延迟加载和关联表的延时加载。
本文主要参考:
- 小傅哥的《重学Java模式》
- 《C语言中文网》设计模式的相关内容
- 《设计模式之禅》第二版 秦小波