一、只如初见

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

组合模式_百度百科组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构。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 {
	
	/**
	 * 实现基本基本决策逻辑
	 */
    @Override
    public Long filter(String matterValue, List<TreeNodeLink> treeNodeLinkList) {
        for (TreeNodeLink nodeLine : treeNodeLinkList) {
            if (decisionLogic(matterValue, nodeLine)) return nodeLine.getNodeIdTo();
        }
        return 0L;
    }
    
    /**
     * 让子类必须实现获取决策值的方法用于对比获取最终的结果
     */
    @Override
    public 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 {

	@Override
	public 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 {

    @Override
    public 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);

    @Override
    public 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();
        // 规则树根ID
        Long 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 {

    @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());
    }

}

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;

    @Before
    public void 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("channel");
        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("0001");

        // 链接:1->12
        TreeNodeLink 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);

        // 节点: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("userSalesVolume");
        treeNode_11.setRuleDesc("销售额A");

        // 链接: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("userSalesVolume");
        treeNode_12.setRuleDesc("销售额B");

        // 链接: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);

        treeRich = new TreeRich(treeRoot, treeNodeMap);

    }

    @Test
    public 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));

    }

    @Test
    public 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.aggregatesTreeRich

规则树聚合

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

cn.test.design.domain.model.voEngineResult决策结果
cn.test.design.domain.model.voTreeNode规则树节点信息
cn.test.design.domain.model.voTreeNodeLink规则树线信息 链接叶子节点和果实节点
cn.test.design.domain.model.voTreeRoot树根信息

规则树聚合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;
	// 用户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;
	}
}

规则树节点信息 TreeNode

package cn.test.design.domain.model.vo;

import java.util.List;

/**
 * 规则树节点信息
 */
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;
	}

}

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

package cn.test.design.domain.model.vo;

/**
 * 规则树线信息 链接叶子节点和果实节点
 */
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;
	}
}

树根信息 TreeRoot

package cn.test.design.domain.model.vo;

/**
 * 树根信息
 */
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;
	}
}

五、扬帆起航

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

本文参考:

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

  百度百科。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐