设计模式-组合模式(决策树)

news2025/7/19 16:55:33

一、只如初见

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

组合模式_百度百科组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构。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设计模式》-付政委。

  百度百科。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/37837.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【活动预告】金融大数据治理实践分享(12/03)

原创 DAMA数据管理 # 本期主题 金融大数据治理实践分享 数字化时代&#xff0c;数据的价值受到越来越多的关注&#xff0c;有人将其比作黄金&#xff0c;也有人将其比作石油&#xff0c;成为组织中的最重要资产之一。针对数据这种有特殊属性的资产&#xff0c;也存在着采集…

[论文阅读] 颜色迁移-N维pdf迁移

[论文阅读] 颜色迁移-N维pdf迁移 文章: N-Dimensional Probability Density Function Transfer and its Application to Colour Transfer, [paper ][code] 1-算法原理 简单来说, 本文将图像看作是随机变量的一组样本, 图像之间的颜色迁移可以看作是样本之间分布的迁移. 因而…

G1D23-RAGA报名蓝桥Attackg安装cudatorch

昨天太摸鱼啦~不过蛮开心的哈哈 今天主要是把积累的ddl都清理一下&#xff01;&#xff01;&#xff01;第一项就是我和舍友一起读的论文嘿嘿&#xff01;&#xff01; 一、RAGA &#xff08;零&#xff09;总结&#xff08;仅模型&#xff09; 作为数据挖掘顶会2021年的论文…

【MATLAB教程案例46】三维数据的插值和滤波处理matlab仿真

欢迎订阅《FPGA学习入门100例教程》、《MATLAB学习入门100例教程》 本课程学习成果预览: 目录 1.软件版本 2.三维数据插值

openFeign夺命连环9问,这谁受得了?

1、前言 前面介绍了Spring Cloud 中的灵魂摆渡者Nacos&#xff0c;和它的前辈们相比不仅仅功能强大&#xff0c;而且部署非常简单。 今天介绍一款服务调用的组件&#xff1a;OpenFeign&#xff0c;同样是一款超越先辈&#xff08;Ribbon、Feign&#xff09;的狠角色。 文章目…

linux 安装新版傻妞+TG+青龙

一键安装命令 #国内服务器要先设置网络代理set sillyGirl download_prefix https://yanyu.ltd/#一键安装命令ssillyGirl;aarm64;if [[ $(uname -a | grep "x86_64") ! "" ]];then aamd64;fi ;if [ ! -d $s ];then mkdir $s;fi ;cd $s;wget https://yanyu.…

git回滚指定版本相关操作

当提交推送到远程仓库之后&#xff0c;需要回退到特定版本,去修改该代码,然后在推送到远程仓库; 1.查看目前版本状态: git status 2.查看提交日志&#xff0c;找到需要回滚的git版本号 git log 3.将当前分支回滚到id9c45732c5701fc84164bebe3c05760a72a4ece12 #这个是软回滚&am…

一个基于容斥原理的概率模型

为论述概率模型的思想&#xff0c;本部分以下图所描述的情况作为例子讲述&#xff0c;为简化图省略线路开关。 不同于单供网络&#xff0c;双供网络由于多条联络线&#xff0c;存在多个扩展供电方案。设供电路径P(p1,p2,..,pn)P(p_1,p_2,..,p_n)P(p1​,p2​,..,pn​)&#xff…

wodFtpDLX ActiveX 组件--Crack

wodFtpDLX ActiveX 组件 FTP 组件&#xff0c;安全&#xff08;SSL、SSH&#xff09;FTP ActiveX 客户端 FtpDLX 组件是 FTP&#xff08;或者更确切地说&#xff0c;文件传输&#xff09;客户端组件。它不仅提供老式的 FTP 协议&#xff0c;还允许您使用安全的 SFTP&#xff08…

短视频怎么在平台规则之内更快更好的吸引用户粉丝的关注

短视频怎么在平台规则之内更快更好的吸引用户粉丝的关注 每天一组短视频运营小技巧&#xff0c;碎片化学习优化自己的账号&#xff0c;今天来学习内容发布技巧&#xff1a; 内容上: 关心用户喜欢看什么 &#xff0c;在视频中埋下泪点笑点吐槽点以及所有你能想到的可以激发观众…

浅谈Linux系统信息与资源

大家将来应用开发Linux程序&#xff0c;无论是ARM架构的板子&#xff0c;还是在Linux上开发应用程序&#xff0c;相信大家都会用到到一些系统相关的信息&#xff0c;譬如时间、日期、以及其它一些系统相关信息&#xff0c;今天带大家了解一下如何通过 Linux 系统调用或 C 库函数…

springMVC参数绑定源码分析

一、遇到的问题 1. 在请求时get方法路径传参&#xff0c;接收时&#xff0c;用枚举接收&#xff0c;出现参数绑定错误 请求路径&#xff1a;http://localhost:9104/api/sent/test2?type0 后端代码&#xff1a; GetMapping("/test2")public String openNewFile2(…

基于优先级的时间片轮转调度算法(C语言实现)

已剪辑自: http://www.demodashi.com/demo/15341.html 基于优先级的时间片轮转调度算法 1. PCB结构&#xff08;Block&#xff09; 由此定义如下结构体&#xff1a; typedef struct Block {int processID; // 进程号int priority; // 优先级int status; // 状态double arriv…

PyQt5 JavaScript调用PyQt代码

JavaScript调用PyQt代码JavaScript调用PyQt代码,是指PyQt可以与加载的Web页面进行双向的数据交互。1.创建QWebChannel对象&#xff1a;创建QWebChannel对象&#xff0c;注册一个需要桥接的对象&#xff0c;以便Web页面的JavaScript使用。其核心代码如下&#xff1a;channel QW…

JUC并发编程与源码分析笔记01-本课程前置知识及要求说明

JUC是什么 JUC是指java.util.concurrent包&#xff0c;在并发编程中广泛使用。 官方文档搜索java.util.concurrent&#xff0c;可以看到有java.util.concurrent、java.util.concurrent。atomic、java.util.concurrent.locks。 本课程学生对象&#xff08;非零基础&#xff09…

记 linux 系统编译好的exp提权提示无gcc

文章目录CVE-2021-4034 漏洞 polkit 提权在目标linux主机没有gcc的情况下提权&#xff0c;在很多情况下的一些内核漏洞需要在目标主机上使用gcc编译才可以正常运行&#xff0c;在本地编译好的exp如果本地系统与目标主机系统不一致&#xff0c;上传执行很大机会导致系统崩溃如脏…

糟了,线上服务出现OOM了

前言 前一段时间&#xff0c;公司同事的一个线上服务OOM的问题&#xff0c;我觉得挺有意思的&#xff0c;在这里跟大家一起分享一下。 我当时其实也参与了一部分问题的定位。 1 案发现场 他们有个mq消费者服务&#xff0c;在某一天下午&#xff0c;出现OOM了&#xff0c;导…

docker技术简介

目录 概念 命令 数据卷 DockerFile 应用部署 服务编排 私有仓库 概念 Docker 是一个开源的应用容器引擎&#xff0c;而容器技术是一种轻量级虚拟化方案&#xff08;虚拟机太繁重了不够轻量级&#xff09;&#xff0c;Docker的基础是Linux容器&#xff08;LXC&#xff09…

离线安装ceph集群(ceph-13.2.10)

记录&#xff1a;332 场景&#xff1a;在CentOS 7.9操作系统上&#xff0c;使用ceph的rpm-mimic的ceph-13.2.10安装ceph集群。应用ceph对象存储(ceph object store)&#xff1b;应用ceph块设备(ceph block device)&#xff1b;应用ceph文件系统(ceph file system)。 版本&…

数据结构(5)树形结构——二叉搜索树(JAVA代码实现)

5.1.概述 二叉搜索树&#xff0c;也叫二叉查找树、二叉排序树&#xff0c;顾名思义&#xff0c;这种二叉树是专门用来进行数据查找的二叉树。二叉搜索树的查找其实就是二分查找。 二叉搜索树的定义&#xff1a; 二叉搜索树可以为空如果二叉搜索树不为空&#xff0c;那么每个…