一、只如初见

组合模式也许大家第一联想到的就是把两个模块组合起来使用,其实好像是这样也其实不是这样,废话不多说,学习一件新的事物总要先了解一下他的概念,老规矩先上概念(摘自百度百科):

组合模式_百度百科组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构。https://baike.baidu.com/item/%E7%BB%84%E5%90%88%E6%A8%A1%E5%BC%8F/1441281?fromModule=lemma_inlink

看完概念想了一下完全和自己想的不一样,最近在看重学Java设计模式其中就讲到了这篇设计模式,其中讲的很容易理解(没有官方的晦涩哈),把其中的精华拿出来与大家分享;给大家的建议就是可能看一遍觉得似懂非懂,但是写一遍就会发现茅塞顿开。

二、登堂入室

相信大家都玩过积木或者乐高玩具,其中每个零部件都是有自己的职责以及功能的。比如说小汽车光有轮子他是无法行走的,必须得有发动机,底盘,车身,电器设备等等部件组成。

像这种利用ABCD功能组合对外提供服务就类似组合模式。对于代码实现有什么特点?那么这使用有他对于项目有什么好处呢?对于加入他对于团队有什么好处呢?灵魂三问,也许你也有类似的问题在脑海中循环,就像这样子。

开个玩笑让大家紧绷的思维放松一下,下面言归正传。

三、抽丝剥茧

3.1 实现特点

什么?你看完这个还是有点迷糊。确实这样有点官方,如果这样不太好理解可以看下这个图:

这样大概就好解释了,假设产品有一个业务诉求先找到了经理,经理说我知道谁能做这个事,找到了A组长,A组长了解了下需求把具体的任务分配到了对应的工程师;相信看完我说的这些各位大概心里有点朦胧的概念了,已经跃跃欲试了;别着急,我们接着往下看。

3.2加入项目中的优势/对于团队好处

前面我们了解到这种设计模式其实就是一个类似树型结构,递归去查找对应的功能,可以让代码易于扩展和维护,更降低团队的维护成本,最重要的是避免接班的伙伴的吐槽。这时候就有人反驳了,我觉得不尽然,这玩意不就是我加个if else的事么需要搞的这么高深莫测么?有疑问是正常的,我刚开始看的时候也有类似的疑问不过相信大家看完后面的就觉得不会有类似的疑问了。

四、举一反三

前面我们讲到其实这种设计模式也没啥好处,对于实现功能来说就是一个if else 的事,下面我们就来举个例子让大家思考下为什么要用这个例子。

4.1   反例

下面以一个产品与研发的实际业务场景来举例:

日期 需求 程序员(内心独白)
星期一(早上) 哥哥,业务说有一个紧急需求,要在这里加一个判断根据不同渠道发放不同激励。 行吧。(也不难,这里加个判断就能上线)

星期二

(下午)

哥哥,上线效果非常不错,业务说要按不同业务类型的在细分下,激励团队。 也不难。(加个类型判断上线吧)

星期三

(晚上)

喂,睡了吗?业务说这次搞的划分非常不错,还要在细分下按照团队销量来细分下。 行吧,加。(已经意识到 if else有点多了)

星期四

(凌晨)

哇,你们太棒了,上线真快,有一个小要求,需要调整下根据团队中个人销量来细分下激励。 好吧,加。(一大堆if else 要修改)

星期五

(半夜)

喂,坏了,怎么发的激励不对了,业务那边开始投诉了,你快看看是什么问题。 (留下了悔恨的泪水)

代码实现:

public class EngineController {private Logger logger = LoggerFactory.getLogger(EngineController.class);public String process(final String channelId, final String userId, final BigDecimal teamSalesVolume,final BigDecimal salesVolume) {logger.info("ifelse实现方式判断用户结果。channelId:{} userId:{} teamSalesVolume:{} salesVolume:{}", channelId, userId, teamSalesVolume,salesVolume);if ("0001".equals(channelId)) {if (new BigDecimal("1000").compareTo(teamSalesVolume)>0||new BigDecimal("1000").compareTo(salesVolume)>0) {return "奖励A";}if (new BigDecimal("1000").compareTo(teamSalesVolume)<=0||new BigDecimal("1000").compareTo(salesVolume)<=0) {return "奖励B";}}if ("0002".equals(channelId)) {if (new BigDecimal("1000").compareTo(teamSalesVolume)>0||new BigDecimal("1000").compareTo(salesVolume)>0) {return "奖励C";}if (new BigDecimal("1000").compareTo(teamSalesVolume)<=0||new BigDecimal("1000").compareTo(salesVolume)<=0) {return "奖励D";}}return null;}}

从上面的结果来看无疑是最快的方式实现了对应的逻辑,但是后续如果if else 太多了就会变成最终的烂代码对后续的维护和迭代造成不同程度的影响。

4.2 正例

工程目录

下面我们开始按层级讲解对应模块功能以及作用

4.2.1 LogicFilter原始过滤接口

LogicFilter :原始过滤接口,定义了原始行为方法,逻辑决策器方法,获取决策值方法,每一个提供决策能力的节点必须实现此接口。

package cn.test.design.domain.service.logic;import java.util.List;
import java.util.Map;import cn.test.design.domain.model.vo.TreeNodeLink;/*** 原始决策过滤器*/
public interface LogicFilter {/*** 逻辑决策器** @param matterValue          决策值* @param treeNodeLineInfoList 决策节点* @return 下一个节点Id*/Long filter(String matterValue, List<TreeNodeLink> treeNodeLineInfoList);/*** 获取决策值** @param decisionMatter 决策物料* @return 决策值*/String matterValue(Long treeId, String userId, Map<String, String> decisionMatter);}

4.2.2 BaseLogic抽象基本实现

BaseLogic:抽象基本实现了LogicFilter 中基本的决策逻辑根据不同类型大于等于,小于等判断逻辑,同时定义了抽象方法让实现他的类都必须实现获取决策值方法,这个决策值用于逻辑对比。

package cn.test.design.domain.service.logic;import java.util.List;
import java.util.Map;import cn.test.design.domain.model.vo.TreeNodeLink;/*** 过滤器抽象基本实现实现,实现基本基本决策逻辑*/
public abstract class BaseLogic implements LogicFilter {/*** 实现基本基本决策逻辑*/@Overridepublic Long filter(String matterValue, List<TreeNodeLink> treeNodeLinkList) {for (TreeNodeLink nodeLine : treeNodeLinkList) {if (decisionLogic(matterValue, nodeLine)) return nodeLine.getNodeIdTo();}return 0L;}/*** 让子类必须实现获取决策值的方法用于对比获取最终的结果*/@Overridepublic abstract String matterValue(Long treeId, String userId, Map<String, String> 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) < 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;}}}

4.2.3 树根节点实现(ChannelGenderFilter,UserSalesVolumeFilter)

这里我们目前业务场景只用到了渠道与销售额所以目前只定义了两种如果实际需要可以定义更多哦;当然目前是直接获取物料值,实际开发中肯定是根据配置文件或从rpc接口或从数据库中获取对应需要的信息。

ChannelGenderFilter:渠道节点

package cn.test.design.domain.service.logic.impl;import java.util.Map;import cn.test.design.domain.service.logic.BaseLogic;/*** * 渠道基本过滤器**/
public class ChannelGenderFilter extends BaseLogic {@Overridepublic String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {// 目前是直接获取物料值,实际开发中肯定是根据配置文件或从rpc接口或从数据库中获取对应需要的信息。return decisionMatter.get("channel");}}

UserSalesVolumeFilter:用户销售额

package cn.test.design.domain.service.logic.impl;import java.util.Map;import cn.test.design.domain.service.logic.BaseLogic;
/*** 用户销售额过滤器**/
public class UserSalesVolumeFilter extends BaseLogic {@Overridepublic String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {// 目前是直接获取物料值,实际开发中肯定是根据配置文件或从rpc接口或从数据库中获取对应需要的信息.return decisionMatter.get("userSalesVolume");}}

4.2.4 IEngine决策引擎

提供给调用方的决策引擎接口,定义好了对应的方法后续如果有新的决策逻辑可以实现对应方法。

package cn.test.design.domain.service.engine;import java.util.Map;import cn.test.design.domain.model.aggregates.TreeRich;
import cn.test.design.domain.model.vo.EngineResult;/*** 决策引擎接口**/
public interface IEngine {EngineResult process(final Long treeId, final String userId, TreeRich treeRich, final Map<String, String> decisionMatter);}

4.2.5 EngineConfig 决策节点配置

这里的决策配置目前是固定的两个树根节点实现(ChannelGenderFilter,UserSalesVolumeFilter)实际场景是把需要的实现配置到表中,可以方便在运营后台配置。

package cn.test.design.domain.service.engine;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;import cn.test.design.domain.service.logic.LogicFilter;
import cn.test.design.domain.service.logic.impl.ChannelGenderFilter;
import cn.test.design.domain.service.logic.impl.UserSalesVolumeFilter;/*** * 决策节点配置,此处可以配置至数据库中方便后续界面操作维护**/
public class EngineConfig {static Map<String, LogicFilter> logicFilterMap;static {logicFilterMap = new ConcurrentHashMap<>();logicFilterMap.put("channel", new ChannelGenderFilter());logicFilterMap.put("userSalesVolume", new UserSalesVolumeFilter());}public Map<String, LogicFilter> getLogicFilterMap() {return logicFilterMap;}@SuppressWarnings("static-access")public void setLogicFilterMap(Map<String, LogicFilter> logicFilterMap) {this.logicFilterMap = logicFilterMap;}}

4.2.6 EngineBase 基础决策引擎

这里是主要决策树处理流程,有点像找树叶的果实一样。同时实现了IEngine 基础决策引擎

但是只是提供了抽像并没有实现,由最终的决策实现类引擎实现。

package cn.test.design.domain.service.engine;import java.util.Map;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import cn.test.design.domain.model.aggregates.TreeRich;
import cn.test.design.domain.model.vo.EngineResult;
import cn.test.design.domain.model.vo.TreeNode;
import cn.test.design.domain.model.vo.TreeRoot;
import cn.test.design.domain.service.logic.LogicFilter;/*** 决策树引擎*/
public abstract class EngineBase extends EngineConfig implements IEngine {private Logger logger = LoggerFactory.getLogger(EngineBase.class);@Overridepublic abstract EngineResult process(Long treeId, String userId, TreeRich treeRich, Map<String, String> decisionMatter);protected TreeNode engineDecisionMaker(TreeRich treeRich, Long treeId, String userId, Map<String, String> decisionMatter) {TreeRoot treeRoot = treeRich.getTreeRoot();Map<Long, TreeNode> treeNodeMap = treeRich.getTreeNodeMap();// 规则树根IDLong rootNodeId = treeRoot.getTreeRootNodeId();TreeNode treeNodeInfo = treeNodeMap.get(rootNodeId);//循环获取对应的处理节点直到获取到对应的结果//节点类型[NodeType];1子叶、2果实 我们只需要使用果实while (treeNodeInfo.getNodeType().equals(1)) {//对应处理规则key用于获取对应实现LogicFilter 的处理节点主要用来获取物料中用于对比的相关值String ruleKey = treeNodeInfo.getRuleKey();//实际的处理节点LogicFilter logicFilter = logicFilterMap.get(ruleKey);String matterValue = logicFilter.matterValue(treeId, userId, decisionMatter);//基础决策抽象中的能力,使用物料值判断使用那个决策节点(获取的是下一个节点)Long nextNode = logicFilter.filter(matterValue, treeNodeInfo.getTreeNodeLinkList());//赋值对应的处理节点,直到找到期望的处理节点(我们这里是最终的果实节点)treeNodeInfo = treeNodeMap.get(nextNode);logger.info("决策树引擎=>{} userId:{} treeId:{} treeNode:{} ruleKey:{} matterValue:{}", treeRoot.getTreeName(), userId, treeId, treeNodeInfo.getTreeNodeId(), ruleKey, matterValue);}return treeNodeInfo;}}

4.2.7 TreeEngineHandle 基础决策引擎实现

这里基本都比较简单直接使用用户传递的对应值传入对应方法即可获取到对应结果

package cn.test.design.domain.service.engine.impl;import java.util.Map;import cn.test.design.domain.model.aggregates.TreeRich;
import cn.test.design.domain.model.vo.EngineResult;
import cn.test.design.domain.model.vo.TreeNode;
import cn.test.design.domain.service.engine.EngineBase;
/*** 基础决策引擎实现**/
public class TreeEngineHandle extends EngineBase {@Overridepublic 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());}}

4.2.8 单元测试

这里的初始化配置目前均为手动配置,实际代码可以使用数据库配置动态配置所需要的节点信息。

package cn.test.design.test;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import com.alibaba.fastjson.JSON;import cn.test.design.domain.model.aggregates.TreeRich;
import cn.test.design.domain.model.vo.EngineResult;
import cn.test.design.domain.model.vo.TreeNode;
import cn.test.design.domain.model.vo.TreeNodeLink;
import cn.test.design.domain.model.vo.TreeRoot;
import cn.test.design.domain.service.engine.IEngine;
import cn.test.design.domain.service.engine.impl.TreeEngineHandle;public class ApiTest {private Logger logger = LoggerFactory.getLogger(ApiTest.class);private TreeRich treeRich;@Beforepublic void init() {// 节点:1TreeNode treeNode_01 = new TreeNode();treeNode_01.setTreeId(10001L);treeNode_01.setTreeNodeId(1L);treeNode_01.setNodeType(1);treeNode_01.setNodeValue(null);treeNode_01.setRuleKey("channel");treeNode_01.setRuleDesc("渠道");// 链接:1->11TreeNodeLink treeNodeLink_11 = new TreeNodeLink();treeNodeLink_11.setNodeIdFrom(1L);treeNodeLink_11.setNodeIdTo(11L);treeNodeLink_11.setRuleLimitType(1);treeNodeLink_11.setRuleLimitValue("0001");// 链接:1->12TreeNodeLink treeNodeLink_12 = new TreeNodeLink();treeNodeLink_12.setNodeIdTo(1L);treeNodeLink_12.setNodeIdTo(12L);treeNodeLink_12.setRuleLimitType(1);treeNodeLink_12.setRuleLimitValue("0002");List<TreeNodeLink> treeNodeLinkList_1 = new ArrayList<>();treeNodeLinkList_1.add(treeNodeLink_11);treeNodeLinkList_1.add(treeNodeLink_12);treeNode_01.setTreeNodeLinkList(treeNodeLinkList_1);// 节点:11TreeNode treeNode_11 = new TreeNode();treeNode_11.setTreeId(10001L);treeNode_11.setTreeNodeId(11L);treeNode_11.setNodeType(1);treeNode_11.setNodeValue(null);treeNode_11.setRuleKey("userSalesVolume");treeNode_11.setRuleDesc("销售额A");// 链接:11->111TreeNodeLink treeNodeLink_111 = new TreeNodeLink();treeNodeLink_111.setNodeIdFrom(11L);treeNodeLink_111.setNodeIdTo(111L);treeNodeLink_111.setRuleLimitType(3);treeNodeLink_111.setRuleLimitValue("25");// 链接:11->112TreeNodeLink 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);// 节点:12TreeNode treeNode_12 = new TreeNode();treeNode_12.setTreeId(10001L);treeNode_12.setTreeNodeId(12L);treeNode_12.setNodeType(1);treeNode_12.setNodeValue(null);treeNode_12.setRuleKey("userSalesVolume");treeNode_12.setRuleDesc("销售额B");// 链接:12->121TreeNodeLink treeNodeLink_121 = new TreeNodeLink();treeNodeLink_121.setNodeIdFrom(12L);treeNodeLink_121.setNodeIdTo(121L);treeNodeLink_121.setRuleLimitType(3);treeNodeLink_121.setRuleLimitValue("25");// 链接:12->122TreeNodeLink 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);// 节点:111TreeNode treeNode_111 = new TreeNode();treeNode_111.setTreeId(10001L);treeNode_111.setTreeNodeId(111L);treeNode_111.setNodeType(2);treeNode_111.setNodeValue("果实A");// 节点:112TreeNode treeNode_112 = new TreeNode();treeNode_112.setTreeId(10001L);treeNode_112.setTreeNodeId(112L);treeNode_112.setNodeType(2);treeNode_112.setNodeValue("果实B");// 节点:121TreeNode treeNode_121 = new TreeNode();treeNode_121.setTreeId(10001L);treeNode_121.setTreeNodeId(121L);treeNode_121.setNodeType(2);treeNode_121.setNodeValue("果实C");// 节点:122TreeNode 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);treeRich = new TreeRich(treeRoot, treeNodeMap);}@Testpublic void test_tree() {logger.info("决策树组合结构信息:\r\n" + JSON.toJSONString(treeRich));IEngine treeEngineHandle = new TreeEngineHandle();/*** 测试数据* 果实A:gender=man、age=22* 果实B:gender=man、age=29* 果实C:gender=woman、age=22* 果实D:gender=woman、age=29*/Map<String, String> decisionMatter = new HashMap<>();decisionMatter.put("channel", "0001");decisionMatter.put("userSalesVolume", "29");EngineResult result = treeEngineHandle.process(10001L, "Oli09pLkdjh", treeRich, decisionMatter);logger.info("测试结果:{}", JSON.toJSONString(result));}@Testpublic void t() {System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01000011", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01101111", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01100100", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01101001", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01101110", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01100111", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01001000", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01100101", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01101100", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01110000", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01110011", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01001100", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01101001", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01100110", 2)).trim()));System.out.println(hexStringToString(String.format("%21X", Long.parseLong("01100101", 2)).trim()));}public static String hexStringToString(String s) {if (s == null || s.equals("")) {return null;}s = s.replace(" ", "");byte[] baKeyword = new byte[s.length() / 2];for (int i = 0; i < baKeyword.length; i++) {try {baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));} catch (Exception e) {e.printStackTrace();}}try {s = new String(baKeyword, "UTF-8");new String();} catch (Exception e1) {e1.printStackTrace();}return s;}
}

4.2.9 相关基础节点类

这里写下相关基础类

cn.test.design.domain.model.aggregates TreeRich

规则树聚合

组织树各节点(叶子,果实)

cn.test.design.domain.model.vo EngineResult 决策结果
cn.test.design.domain.model.vo TreeNode 规则树节点信息
cn.test.design.domain.model.vo TreeNodeLink 规则树线信息 链接叶子节点和果实节点
cn.test.design.domain.model.vo TreeRoot 树根信息

规则树聚合TreeRich

package cn.test.design.domain.model.aggregates;import java.util.Map;import cn.test.design.domain.model.vo.TreeNode;
import cn.test.design.domain.model.vo.TreeRoot;/*** 规则树聚合*/
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;}
}

决策结果EngineResult

package cn.test.design.domain.model.vo;/*** 决策结果*/
public class EngineResult {// 执行结果private boolean isSuccess;// 用户IDprivate String userId;// 规则树IDprivate Long treeId;// 果实节点IDprivate 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;}
}

规则树节点信息 TreeNode

package cn.test.design.domain.model.vo;import java.util.List;/*** 规则树节点信息*/
public class TreeNode {// 规则树IDprivate Long treeId;// 规则树节点IDprivate Long treeNodeId;// 节点类型;1叶子、2 果实private Integer nodeType;// 节点值[nodeType=2];果实值private String nodeValue;// 规则Keyprivate 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;}}

规则树线信息 链接叶子节点和果实节点 TreeNodeLink

package cn.test.design.domain.model.vo;/*** 规则树线信息 链接叶子节点和果实节点*/
public class TreeNodeLink {// 节点Fromprivate Long nodeIdFrom;// 节点Toprivate 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;}
}

树根信息 TreeRoot

package cn.test.design.domain.model.vo;/*** 树根信息*/
public class TreeRoot {// 规则树IDprivate Long treeId;// 规则树根IDprivate 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;}
}

五、扬帆起航

写在最后是不是发现这种模式就像电脑主机一样不同的接口是可以组合拔插的也可以随时配置不同的配置,这种模式保证了开闭原则只要前期定义好对应的结构,每次新增诉求只需要新增对应的节点即可,或者都可以做为运营后台模式由运营动态配置对应的路由规则便于后续维护。

本文参考:

《重学java设计模式》-付政委。

百度百科。

设计模式-组合模式(决策树)相关推荐

  1. 设计模式---组合模式

    设计模式---组合模式 什么是组合模式:Composite? 使用场景 代码示例 组合模式模板 组合模式的安全性和透明性 总结 优缺点: 适用场景: 什么是组合模式:Composite? 计算机的文件 ...

  2. Java设计模式 —— 组合模式(Composite)

    Java设计模式 -- 组合模式(Composite) 定义 Composite,组合模式:将对象组合成树形结构以表示部分整体的关系,Composite使得用户对单个对象和组合对象的使用具有一致性. ...

  3. JS设计模式--组合模式

    JS设计模式–组合模式 昨天学习了白贺翔老师的JS组合模式,现在把我学到的分享出来叭O(∩_∩)O,直接看下面代码 <!DOCTYPE html> <html lang=" ...

  4. 设计模式----组合模式UML和实现代码

    2019独角兽企业重金招聘Python工程师标准>>> 一.什么是组合模式? 组合模式(Composite)定义:将对象组合成树形结构以表示'部分---整体'的层次结构.组合模式使得 ...

  5. 大话设计模式—组合模式

    组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象.组合模式依据树形结构来组合对象,用来表示部分以及整体层次.这种类型的设计模式属于结构型模式, ...

  6. java设计模式 组合_JAVA 设计模式 组合模式

    用途组合模式 (Component) 将对象组合成树形结构以表示"部分-整体"的层次结构. 组合模式使得用户对单个对象和组合对象的使用具有唯一性. 组合模式是一种结构型模式. 结构 ...

  7. Java常用设计模式————组合模式

    引言 组合模式,是一种类似递归算法的结构性设计模式,通过以简单的 List ,组合本类对象,实现树状对象结构的"部分.整体"的层次. 它可以让调用程序不需要关心复杂对象与简单对象的 ...

  8. C++设计模式-组合模式

    目录 基本概念 代码与实例 基本概念 个人感觉Qt的对象树就是运用了这种设计模式!!! 当然,只是个人感觉,本人并没有研究Qt的源码 组合模式(Composite):将对象组合成树形结构以表示'部分- ...

  9. [Head First设计模式]生活中学设计模式——组合模式

    系列文章 [Head First设计模式]山西面馆中的设计模式--装饰者模式 [Head First设计模式]山西面馆中的设计模式--观察者模式 [Head First设计模式]山西面馆中的设计模式- ...

最新文章

  1. [逆向基础] C++中基本数据类型的表现形式
  2. CYQ.Data 轻量数据层之路 V2.0 震撼惊世 支持多数据库/内置Aop(二十五)
  3. 程序员们,您还想熬夜吗?
  4. Node.js+Koa开发微信公众号个人笔记(一)准备工作 - ZhangCui - 博客园
  5. Spoken English(015)
  6. Microsoft Visual Studio Code
  7. 张小龙:如何把产品做简单
  8. android xml黑体字_如何在 Android 上使用思源黑体作为系统字体?
  9. ECharts三维图表
  10. MX250和MX350哪个好一点,区别和差距在哪里?
  11. C++基础——模板的0初始化
  12. 微信服务号获取地理位置
  13. android webview加载图片不显示,解决android webview中图片不显示问题
  14. matlab 绘图颜色参考 linspace
  15. 成为虚无鸿蒙系统掌控者,飞剑问道:烟雨飞剑破开鸿蒙空间,秦云成第四位鸿蒙掌控者!...
  16. 「笔耕不辍」MQ的原理以及持久化
  17. vue调用手机扫描二维码
  18. C77 - 不完全扫雷[不定期更新,全凭心情]
  19. raised exception class EAccexxViolation with ‘Access violation at address 45EFD5 in module 出错
  20. 竞技时代引领VR电竞,WVA2018全新出发!

热门文章

  1. python水浒传名字次数_《水浒传》里重名多,是施耐庵取名不用心还是太随意?...
  2. 基于 Python 的大型超市商品销售关联度分析系统
  3. 在Kali中 利用工具Fluxion渗透wpa/wpa2加密WiFi 详细步骤(小白适用) 2020.1
  4. 给想做亚马逊测评的你一些忠告!千万不要被骗了!
  5. 代码报错, Hook script file phys_opt_design.tcl used in Synth Design does not exist.
  6. Spring事务传播性(较详细描述)
  7. 弹幕护体下的B站,走得越来越稳健了
  8. 看板管理解析:如何通过看板提升项目管理效率?
  9. LoRa网关在智慧农业应用
  10. Python己亥杂说2 - 快排