《设计模式入门》 12.组合模式

组合模式有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。

也就是说这种把相似对象(也可以称作是方法)组合成一组可被调用的结构树对象的设计思路叫做组合模式。

讨论组合模式,我们一般会讨论决策树。也就是说我们一般可以把组合模式看成一个链接组织起来的结构树,这种模式一般在结构树的解决方案用的非常多。

一般组合模式由三个角色构成:

  • 抽象构件(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虫洞栈-优快云博客

综上所述:

优点:

  1. 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
  2. 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

缺点:

  1. 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
  2. 不容易限制容器中的构件;
  3. 不容易用继承的方法来增加构件的新功能;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PigeonEssence

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值