组合模式有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。
也就是说这种把相似对象(也可以称作是方法)组合成一组可被调用的结构树对象的设计思路叫做组合模式。
讨论组合模式,我们一般会讨论决策树。也就是说我们一般可以把组合模式看成一个链接组织起来的结构树,这种模式一般在结构树的解决方案用的非常多。
一般组合模式由三个角色构成:
- 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。
- 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。
- 树枝构件(Composite)角色:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法
我们在一个简单的例子里面讨论一下:
=========================================================================
我们来模拟一个关系树,拟定一个根节点连接了两个叶子结点,并且通过根节点访问叶子结点。
=========================================================================
首先,我们创建一个抽象构建进行接口规范:
package CompositePattern.TreeDemo;
import java.util.List;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName Node.java
* @Description 抽象构件(Component)角色
* 为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。
* @createTime 2022年03月07日 15:41:00
*/
public interface NodeComponent {
/**
* 增加节点
* @param component 节点
*/
public void addNode(NodeComponent component);
/**
* 删除节点
* @param component 节点
*/
public void removeNode(NodeComponent component);
/**
* 获取子节点
* @return 子节点
*/
public List<NodeComponent> getChild();
/**
* 操作
*/
public void operation();
}
然后我们创建树叶构建角色。我们把这种模式想象成一个树,那么叶子结点就不存在子节点,他的作用就是存储数据+被访问。
package CompositePattern.TreeDemo;
import java.util.List;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName Node.java
* @Description 叶子节点
* @createTime 2022年03月07日 15:45:00
*/
public class Leaf implements NodeComponent{
private String name;
public Leaf(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void addNode(NodeComponent component) {
}
@Override
public void removeNode(NodeComponent component) {
}
@Override
public List<NodeComponent> getChild() {
//叶子结点没有Child
return null;
}
@Override
public void operation() {
System.out.println("树叶: "+ name +" 被访问!");
}
}
之后创建树枝构件角色,这个与其说是树枝,不如说“关系”,或者说是连接器。
package CompositePattern.TreeDemo;
import java.util.ArrayList;
import java.util.List;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName Composite.java
* @Description 树枝构件(Composite)角色:是组合中的分支节点对象
* @createTime 2022年03月07日 15:50:00
*/
public class Composite implements NodeComponent{
private ArrayList<NodeComponent> children = new ArrayList<NodeComponent>();
private String name;
public Composite(String name){
this.name=name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void addNode(NodeComponent component) {
children.add(component);
}
@Override
public void removeNode(NodeComponent component) {
children.remove(component);
}
@Override
public List<NodeComponent> getChild() {
return children;
}
@Override
public void operation() {
System.out.println("关系:"+name);
for (NodeComponent component : children) {
component.operation();
}
}
}
树枝节点包含了子节点,他的子节点可以是叶子结点,也可以是另一个树枝节点。然后通过复写operation方法,来实现对于自己子节点的遍历。
测试一下:
package CompositePattern.TreeDemo;
import java.util.Iterator;
import java.util.List;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName TreeDemoTest.java
* @Description TODO
* @createTime 2022年03月07日 16:00:00
*/
public class TreeDemoTest {
public static void main(String[] args) {
//创建根节点对象
NodeComponent root = new Composite("根");
//创建两个组合节点对象
NodeComponent relation = new Composite("根和叶结点1的关系");
NodeComponent relation2 = new Composite("根和叶结点2的关系");
//将两个组合节点对象添加到根节点
root.addNode(relation);
root.addNode(relation2);
//创建两个叶子节点
NodeComponent leaf1 = new Leaf("叶结点1");
NodeComponent leaf2 = new Leaf("叶结点2");
//对应关系
relation.addNode(leaf1);
relation2.addNode(leaf2);
//根操作
root.operation();
}
}
=========================================================================
上面是一个简单例子的实现,对于决策树的实现我们可以参考小傅哥的例子:
源码地址:
https://github.com/fuzhengwei/itstack-demo-design/commit/13faa89751490ad1494d65ccf0a09843de7ea5d5ww
我在这上面做了一些注释方便看懂:
=========================================================================
VO层定义:
首先还是定义规则节点:
package CompositePattern.DecisionTreeSimulation.Vo;
import java.util.List;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName TreeNode.java
* @Description 规则树节点信息
* @createTime 2022年03月07日 10:46:00
*/
public class TreeNode {
/**
* 规则树ID
*/
private Long treeId;
/**
* 规则树节点ID
*/
private Long treeNodeId;
/**
*节点类型;1子叶、2果实
*/
private Integer nodeType;
/**
* 节点值[nodeType=2];果实值
*/
private String nodeValue;
/**
* 规则Key
*/
private String ruleKey;
/**
* 规则描述
*/
private String ruleDesc;
/**
* 节点链路
*/
private List<TreeNodeLink> treeNodeLinkList;
public Long getTreeId() {
return treeId;
}
public void setTreeId(Long treeId) {
this.treeId = treeId;
}
public Long getTreeNodeId() {
return treeNodeId;
}
public void setTreeNodeId(Long treeNodeId) {
this.treeNodeId = treeNodeId;
}
public Integer getNodeType() {
return nodeType;
}
public void setNodeType(Integer nodeType) {
this.nodeType = nodeType;
}
public String getNodeValue() {
return nodeValue;
}
public void setNodeValue(String nodeValue) {
this.nodeValue = nodeValue;
}
public String getRuleKey() {
return ruleKey;
}
public void setRuleKey(String ruleKey) {
this.ruleKey = ruleKey;
}
public String getRuleDesc() {
return ruleDesc;
}
public void setRuleDesc(String ruleDesc) {
this.ruleDesc = ruleDesc;
}
public List<TreeNodeLink> getTreeNodeLinkList() {
return treeNodeLinkList;
}
public void setTreeNodeLinkList(List<TreeNodeLink> treeNodeLinkList) {
this.treeNodeLinkList = treeNodeLinkList;
}
}
定义规则树线信息,也就是这个连接是从哪里来,到哪里去。
package CompositePattern.DecisionTreeSimulation.Vo;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName TreeNodeLink.java
* @Description 规则树线信息
* @createTime 2022年03月07日 10:50:00
*/
public class TreeNodeLink {
/**
* 节点From
*/
private Long nodeIdFrom;
/**
* 节点To
*/
private Long nodeIdTo;
/**
* 限定类型;1:=;
* 2:>;
* 3:<;
* 4:>=;
* 5<=;
* 6:enum[枚举范围]
*/
private Integer ruleLimitType;
/**
* 限定值
*/
private String ruleLimitValue;
public Long getNodeIdFrom() {
return nodeIdFrom;
}
public void setNodeIdFrom(Long nodeIdFrom) {
this.nodeIdFrom = nodeIdFrom;
}
public Long getNodeIdTo() {
return nodeIdTo;
}
public void setNodeIdTo(Long nodeIdTo) {
this.nodeIdTo = nodeIdTo;
}
public Integer getRuleLimitType() {
return ruleLimitType;
}
public void setRuleLimitType(Integer ruleLimitType) {
this.ruleLimitType = ruleLimitType;
}
public String getRuleLimitValue() {
return ruleLimitValue;
}
public void setRuleLimitValue(String ruleLimitValue) {
this.ruleLimitValue = ruleLimitValue;
}
}
然后创建一个规则树的聚合,也就是树根和子节点的关系:
package CompositePattern.DecisionTreeSimulation.Vo;
import java.util.Map;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName TreeRich.java
* @Description 规则树聚合
* @createTime 2022年03月07日 10:59:00
*/
public class TreeRich {
/**
* 树根信息
*/
private TreeRoot treeRoot;
/**
* 树节点ID -> 子节点
*/
private Map<Long, TreeNode> treeNodeMap;
public TreeRich(TreeRoot treeRoot, Map<Long, TreeNode> treeNodeMap) {
this.treeRoot = treeRoot;
this.treeNodeMap = treeNodeMap;
}
public TreeRoot getTreeRoot() {
return treeRoot;
}
public void setTreeRoot(TreeRoot treeRoot) {
this.treeRoot = treeRoot;
}
public Map<Long, TreeNode> getTreeNodeMap() {
return treeNodeMap;
}
public void setTreeNodeMap(Map<Long, TreeNode> treeNodeMap) {
this.treeNodeMap = treeNodeMap;
}
}
定义树根节点:
package CompositePattern.DecisionTreeSimulation.Vo;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName TreeRoot.java
* @Description 树根信息
* @createTime 2022年03月07日 10:56:00
*/
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;
}
}
封装决策结果:
package CompositePattern.DecisionTreeSimulation.Vo;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName EngineResult.java
* @Description 决策结果
* @createTime 2022年03月07日 10:58:00
*/
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;
}
}
定义过滤器相关:
决策抽象类提供基础服务:
package CompositePattern.DecisionTreeSimulation.Filter;
import CompositePattern.DecisionTreeSimulation.Vo.TreeNodeLink;
import java.util.List;
import java.util.Map;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName BaseLogic.java
* @Description 决策抽象类提供基础服务:
* 在抽象方法中实现了接口方法,同时定义了基本的决策方法;1、2、3、4、5,等于、小于、大于、小于等于、大于等于的判断逻辑。
* 同时定义了抽象方法,让每一个实现接口的类都必须按照规则提供决策值,这个决策值用于做逻辑比对。
* @createTime 2022年03月07日 10:55:00
*/
public abstract class BaseLogic implements LogicFilter{
/**
* 逻辑决策器
* @param matterValue 决策值
* @param treeNodeLineInfoList 决策节点
* @return 下一个节点ID
*/
@Override
public Long filter(String matterValue, List<TreeNodeLink> treeNodeLineInfoList) {
//遍历决策节点
for (TreeNodeLink nodeLine : treeNodeLineInfoList) {
//根据决策值和决策节点中的对象进行比较
if (decisionLogic(matterValue, nodeLine)) {
//返回节点的节点To,也就是返回下一个节点
return nodeLine.getNodeIdTo();
}
}
//错误返回0
return 0L;
}
/**
* 获取决策值
* @param treeId 节点ID
* @param userId 用户ID
* @param decisionMatter 决策物料
* @return 决策值
*/
@Override
public abstract String matterValue(Long treeId, String userId, Map<String, String> decisionMatter);
/**
* 义了基本的决策方法;1、2、3、4、5,等于、小于、大于、小于等于、大于等于的判断逻辑。
* @param matterValue 决策值
* @param nodeLink 决策节点
* @return 逻辑比对
*/
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) < Double.parseDouble(nodeLink.getRuleLimitValue());
case 4:
//小于等于的逻辑判断
return Double.parseDouble(matterValue) <= Double.parseDouble(nodeLink.getRuleLimitValue());
case 5:
//大于等于的逻辑判断
return Double.parseDouble(matterValue) >= Double.parseDouble(nodeLink.getRuleLimitValue());
default:
return false;
}
}
}
配置通用接口:
package CompositePattern.DecisionTreeSimulation.Filter;
import CompositePattern.DecisionTreeSimulation.Vo.TreeNodeLink;
import java.util.List;
import java.util.Map;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName LogicFilter.java
* @Description 这一部分定义了适配的通用接口,逻辑决策器、获取决策值,让每一个提供决策能力的节点都必须实现此接口,保证统一性。
* @createTime 2022年03月07日 10:43:00
*/
public interface LogicFilter {
/**
* 逻辑决策器
* @param matterValue 决策值
* @param treeNodeLineInfoList 决策节点
* @return 下一个节点ID
*/
Long filter(String matterValue, List<TreeNodeLink> treeNodeLineInfoList);
/**
* 获取决策值
* @param treeId 节点ID
* @param userId 用户ID
* @param decisionMatter 决策物料
* @return 决策值
*/
String matterValue(Long treeId, String userId, Map<String, String> decisionMatter);
}
我们需要对用户的性别和年龄进行划分,于是需要对年龄和性别抽象出节点:
年龄节点:
package CompositePattern.DecisionTreeSimulation.Filter;
import java.util.Map;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName UserAgeFilter.java
* @Description 年龄节点
* @createTime 2022年03月07日 11:06:00
*/
public class UserAgeFilter extends BaseLogic{
/**
* 获取用户的年龄
* @param treeId 节点ID
* @param userId 用户ID
* @param decisionMatter 决策物料
* @return 用户年龄
*/
@Override
public String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {
return decisionMatter.get("age");
}
}
性别节点:
package CompositePattern.DecisionTreeSimulation.Filter;
import java.util.Map;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName UserGenderFilter.java
* @Description 性别节点
* @createTime 2022年03月07日 11:07:00
*/
public class UserGenderFilter extends BaseLogic{
/**
* 获取用户的性别
* @param treeId 节点ID
* @param userId 用户ID
* @param decisionMatter 决策物料
* @return 用户的性别
*/
@Override
public String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {
return decisionMatter.get("gender");
}
}
然后我们对决策引擎进行抽象定义:
package CompositePattern.DecisionTreeSimulation.Engine;
import CompositePattern.DecisionTreeSimulation.Vo.EngineResult;
import CompositePattern.DecisionTreeSimulation.Vo.TreeRich;
import java.util.Map;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MyEngine.java
* @Description 决策引擎接口定义
* 对于使用方来说也同样需要定义统一的接口操作,这样的好处非常方便后续拓展出不同类型的决策引擎,
* 也就是可以建造不同的决策工厂。
* @createTime 2022年03月07日 11:09:00
*/
public interface MyEngine {
/**
* 通过给定的规则决定决策结果
* @param treeId 节点ID
* @param userId 用户ID
* @param treeRich 规则树聚合
* @param decisionMatter 决策物料
* @return 决策结果
*/
EngineResult process(final Long treeId, final String userId, TreeRich treeRich, final Map<String, String> decisionMatter);
}
创建抽象类实现基础引擎功能:
package CompositePattern.DecisionTreeSimulation.Engine;
import CompositePattern.DecisionTreeSimulation.Config.EngineConfig;
import CompositePattern.DecisionTreeSimulation.Vo.EngineResult;
import CompositePattern.DecisionTreeSimulation.Vo.TreeRich;
import CompositePattern.DecisionTreeSimulation.Vo.TreeRoot;
import CompositePattern.DecisionTreeSimulation.Filter.LogicFilter;
import CompositePattern.DecisionTreeSimulation.Vo.TreeNode;
import java.util.Map;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName EngineBase.java
* @Description 基础决策引擎功能:
* 这里主要提供决策树流程的处理过程,有点像通过链路的关系(性别、年龄)在二叉树中寻找果实节点的过程
* @createTime 2022年03月07日 11:15:00
*/
public abstract class EngineBase extends EngineConfig implements MyEngine {
/**
* 决策结果抽象类
* @param treeId 节点ID
* @param userId 用户ID
* @param treeRich 规则树聚合
* @param decisionMatter 决策物料
* @return 决策结果
*/
@Override
public abstract EngineResult process(Long treeId, String userId, TreeRich treeRich, Map<String, String> decisionMatter);
/**
*
* @param treeRich 规则树聚合
* @param treeId 节点ID
* @param userId 用户ID
* @param decisionMatter 决策物料
* @return 节点信息
*/
protected TreeNode engineDecisionMaker(TreeRich treeRich, Long treeId, String userId, Map<String, String> decisionMatter) {
//从规则树聚合中拿到树根信息
TreeRoot treeRoot = treeRich.getTreeRoot();
//获取规则树根ID
Long rootNodeId = treeRoot.getTreeRootNodeId();
//获取子节点map
Map<Long, TreeNode> treeNodeMap = treeRich.getTreeNodeMap();
//通过规则树根ID获取对应的节点信息
TreeNode treeNodeInfo = treeNodeMap.get(rootNodeId);
//节点类型[NodeType];1子叶、2果实: 如果是子叶:
while (treeNodeInfo.getNodeType().equals(1)) {
//规则Key
String ruleKey = treeNodeInfo.getRuleKey();
//从EngineConfig中的Map中获取 logicFilter
LogicFilter logicFilter = logicFilterMap.get(ruleKey);
//获取决策值
String matterValue = logicFilter.matterValue(treeId, userId, decisionMatter);
//通过逻辑决策器拿到下一个节点的ID
Long nextNode = logicFilter.filter(matterValue, treeNodeInfo.getTreeNodeLinkList());
//通过下一个节点的ID获取新的节点信息
treeNodeInfo = treeNodeMap.get(nextNode);
System.out.println("决策树引擎=> " + treeRoot.getTreeName() + "\n userId:" + userId + "\n treeId:" + treeId + "\n treeNode:" + treeNodeInfo.getTreeNodeId() + "\n t_ruleKey:" + ruleKey + "\n matterValue:" + matterValue);
}
return treeNodeInfo;
}
}
实现决策引擎:
package CompositePattern.DecisionTreeSimulation.Engine;
import CompositePattern.DecisionTreeSimulation.Vo.EngineResult;
import CompositePattern.DecisionTreeSimulation.Vo.TreeRich;
import CompositePattern.DecisionTreeSimulation.Vo.TreeNode;
import java.util.Map;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName TreeEngineHandle.java
* @Description 决策引擎的实现
* 通过传递进来的必要信息;决策树信息、决策物料值,来做具体的树形结构决策。
* @createTime 2022年03月07日 13:38:00
*/
public class TreeEngineHandle extends EngineBase{
@Override
public EngineResult process(Long treeId, String userId, TreeRich treeRich, Map<String, String> decisionMatter) {
// 决策流程
TreeNode treeNode = engineDecisionMaker(treeRich, treeId, userId, decisionMatter);
// 决策结果
return new EngineResult(userId, treeId, treeNode.getTreeNodeId(), treeNode.getNodeValue());
}
}
决策引擎的节点配置:
package CompositePattern.DecisionTreeSimulation.Config;
import CompositePattern.DecisionTreeSimulation.Filter.LogicFilter;
import CompositePattern.DecisionTreeSimulation.Filter.UserAgeFilter;
import CompositePattern.DecisionTreeSimulation.Filter.UserGenderFilter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName EngineConfig.java
* @Description 决策节点配置:
* 在这里将可提供服务的决策节点配置到map结构中,
* 对于这样的map结构可以抽取到数据库中,那么就可以非常方便的管理。
* @createTime 2022年03月07日 11:13:00
*/
public class EngineConfig {
public static Map<String, LogicFilter> logicFilterMap;
static {
logicFilterMap = new ConcurrentHashMap<>();
logicFilterMap.put("userAge", new UserAgeFilter());
logicFilterMap.put("userGender", new UserGenderFilter());
}
public Map<String, LogicFilter> getLogicFilterMap() {
return logicFilterMap;
}
public void setLogicFilterMap(Map<String, LogicFilter> logicFilterMap) {
EngineConfig.logicFilterMap = logicFilterMap;
}
}
测试一下:
package CompositePattern.DecisionTreeSimulation;
import CompositePattern.DecisionTreeSimulation.Engine.MyEngine;
import CompositePattern.DecisionTreeSimulation.Engine.TreeEngineHandle;
import CompositePattern.DecisionTreeSimulation.Vo.*;
import com.alibaba.fastjson.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName CompositeTest.java
* @Description 测试类
* @createTime 2022年03月07日 13:40:00
*/
public class CompositeTest {
public static void main(String[] args) {
testTree(init());
}
/**
* 根据决策树执行具体方法
* @param treeRich 决策树
*/
public static void testTree(TreeRich treeRich) {
System.out.println("决策树信息:\r\n"+ JSONObject.toJSONString(treeRich)+"\n");
MyEngine treeEngineHandle = new TreeEngineHandle();
Map<String, String> decisionMatter = new HashMap<>();
decisionMatter.put("gender", "man");
decisionMatter.put("age", "29");
EngineResult result = treeEngineHandle.process(10001L, "Oli09pLkdjh", treeRich, decisionMatter);
System.out.println(JSONObject.toJSONString(result));
}
/**
* 添加决策树信息并初始化决策树
* @return 决策树
*/
public static TreeRich init() {
// 节点:1
TreeNode treeNode_01 = new TreeNode();
treeNode_01.setTreeId(10001L);
treeNode_01.setTreeNodeId(1L);
treeNode_01.setNodeType(1);
treeNode_01.setNodeValue(null);
treeNode_01.setRuleKey("userGender");
treeNode_01.setRuleDesc("用户性别[男/女]");
// 链接:1->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<TreeNodeLink> 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<TreeNodeLink> 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<TreeNodeLink> 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<Long, TreeNode> 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);
return new TreeRich(treeRoot, treeNodeMap);
}
}
对于第二个例子,有兴趣可以去原文进行更深入的了解。
重学 Java 设计模式:实战组合模式「营销差异化人群发券,决策树引擎搭建场景」_bugstack虫洞栈-优快云博客
综上所述:
优点:
- 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
- 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;
缺点:
- 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
- 不容易限制容器中的构件;
- 不容易用继承的方法来增加构件的新功能;