链式多分支规则树模型的应用

 目录

开始调用

初始化 


欢迎关注我的博客!26届java选手,一起加油💘💦👨‍🎓😄😂

引入

最近在学习一个项目中的链式多分枝规则树模型的使用,模型如下:

如图所示:

这是一种 链式多分支规则树模型 设计模式,核心是通过功能节点自主决策后续流程执行链路,相比责任链模式,它允许更灵活的分支扩展,每个节点像 “决策者” 一样根据规则选择下一步走向。以下是核心组件拆解:

StrategyMapper、StrategyHandler、AbstractStrategyRouter定义在type层,与业务层隔离,然后将DefaultActivityStrategyFactory、AbstractGroupBuyMarketSupport与各节点定义在业务层进行业务逻辑处理。

  • 策略映射器(StrategyMapper)与策略处理器(StrategyHandler)
    实现抽象类 AbstractStrategyRouter,前者负责策略映射(如定义不同场景对应规则),后者处理具体策略逻辑(如执行规则计算)。
  • 策略路由抽象类(AbstractStrategyRouter)
    定义路由规则的抽象框架,规范策略映射、处理的通用逻辑,是整个流程的 “规则模板”。
  • 策略工厂(DefaultActivityStrategyFactory)
    作为 “对象制造工厂”,负责创建拼团活动相关的策略实例,确保策略对象的统一管理与创建。
  • 功能服务支撑类(AbstractGroupBuyMarketSupport)
    提供底层通用服务(如数据校验、基础计算),像 “后勤保障”,供上层节点流程调用。
  • 节点体系(RootNode、SwitchRoot 等)
    • 根节点(RootNode):流程起点,类似 “入口”。
    • 开关节点(SwitchRoot):核心决策点,根据条件(如用户类型、活动规则)选择分支(走向其他节点或默认分支)。
    • 营销节点(MarketNode):处理营销相关逻辑(如优惠计算)。
    • 结尾节点(EndNode):流程终点,标志链路结束。

开始调用

    @Resourceprivate DefaultActivityStrategyFactory defaultActivityStrategyFactory;@Overridepublic TrialBalanceEntity indexMarketTrial(MarketProductEntity marketProductEntity) throws Exception {// 获取执行策略StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> strategyHandler = defaultActivityStrategyFactory.strategyHandler();// 受理试算操作return strategyHandler.apply(marketProductEntity, new DefaultActivityStrategyFactory.DynamicContext());}

初始化

起始点为DefaultActivityStrategyFactory 策略工厂 ,返回了rootNode给我们,也就是此时的 strategyHandler是rootNode类型的,如下:

@Service
public class DefaultActivityStrategyFactory {private final RootNode rootNode;public DefaultActivityStrategyFactory(RootNode rootNode) {this.rootNode = rootNode;}public StrategyHandler<MarketProductEntity, DynamicContext, TrialBalanceEntity> strategyHandler() {return rootNode;}@Data@Builder@AllArgsConstructor@NoArgsConstructorpublic static class DynamicContext {// 拼团活动营销配置值对象private GroupBuyActivityDiscountVO groupBuyActivityDiscountVO;// 商品信息private SkuVO skuVO;// 折扣金额private BigDecimal deductionPrice;// 支付金额private BigDecimal payPrice;// 活动可见性限制private boolean visible;// 活动private boolean enable;}}

执行流程

// 受理试算操作return strategyHandler.apply(marketProductEntity, new DefaultActivityStrategyFactory.DynamicContext());

rootNode继承自AbstractGroupBuyMarketSupport——功能服务支撑类,AbstractGroupBuyMarketSupport又继承自AbstractMultiThreadStrategyRouter——策略路由抽象类,就会去执行下面这里的apply方法,

public abstract class AbstractMultiThreadStrategyRouter<T, D, R> implements StrategyMapper<T, D, R>, StrategyHandler<T, D, R> {@Getter@Setterprotected StrategyHandler<T, D, R> defaultStrategyHandler = StrategyHandler.DEFAULT;public R router(T requestParameter, D dynamicContext) throws Exception {StrategyHandler<T, D, R> strategyHandler = get(requestParameter, dynamicContext);if(null != strategyHandler) return strategyHandler.apply(requestParameter, dynamicContext);return defaultStrategyHandler.apply(requestParameter, dynamicContext);}@Overridepublic R apply(T requestParameter, D dynamicContext) throws Exception {// 异步加载数据multiThread(requestParameter, dynamicContext);// 业务流程受理return doApply(requestParameter, dynamicContext);}/*** 异步加载数据*/protected abstract void multiThread(T requestParameter, D dynamicContext) throws ExecutionException, InterruptedException, TimeoutException;/*** 业务流程受理*/protected abstract R doApply(T requestParameter, D dynamicContext) throws Exception;}

会先去AbstractGroupBuyMarketSupport看看有没有重写multiThread和doApply方法,

public abstract class AbstractGroupBuyMarketSupport<MarketProductEntity, DynamicContext, TrialBalanceEntity> extends AbstractMultiThreadStrategyRouter<cn.bugstack.domain.activity.model.entity.MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, cn.bugstack.domain.activity.model.entity.TrialBalanceEntity> {protected long timeout = 500;@Resourceprotected IActivityRepository repository;@Overrideprotected void multiThread(cn.bugstack.domain.activity.model.entity.MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws ExecutionException, InterruptedException, TimeoutException {// 缺省的方法}}

当我们的rootNode不想用到多线程加载数据的时候就没有重写这个方法,为空,但是rootNode重写了doApply方法,也就是在这里处理rootNode想要处理的业务,

@Slf4j
@Service
public class RootNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> {@Resourceprivate SwitchNode switchNode;@Overrideprotected TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {log.info("商品查询试算服务-RootNode userId:{} requestParameter:{}", requestParameter.getUserId(), JSON.toJSONString(requestParameter));// 参数判断if (StringUtils.isBlank(requestParameter.getUserId()) || StringUtils.isBlank(requestParameter.getGoodsId()) ||StringUtils.isBlank(requestParameter.getSource()) || StringUtils.isBlank(requestParameter.getChannel())) {throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());}return router(requestParameter, dynamicContext);}@Overridepublic StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {return switchNode;}}

执行完doApply方法,就执行return router()方法,这里带上请求参数和上下文对象,router方法在AbstractMultiThreadStrategyRouter类中,负责流转节点

 public R router(T requestParameter, D dynamicContext) throws Exception {StrategyHandler<T, D, R> strategyHandler = get(requestParameter, dynamicContext);if(null != strategyHandler) return strategyHandler.apply(requestParameter, dynamicContext);return defaultStrategyHandler.apply(requestParameter, dynamicContext);}

这里调用的get是StrategyMapper——策略映射器的get方法,因为当前对象是rootNode,如果rootNode实现了get就会回到rootNode的get中

public interface StrategyMapper<T, D, R> {/*** 获取待执行策略** @param requestParameter 入参* @param dynamicContext   上下文* @return 返参* @throws Exception 异常*/StrategyHandler<T, D, R> get(T requestParameter, D dynamicContext) throws Exception;}

回到rootNode,这里重写了get,也就是返回我们需要从rootNode去往的下一个节点

public class RootNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> {@Resourceprivate SwitchNode switchNode;@Overrideprotected TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {log.info("拼团商品查询试算服务-RootNode userId:{} requestParameter:{}", requestParameter.getUserId(), JSON.toJSONString(requestParameter));// 参数判断if (StringUtils.isBlank(requestParameter.getUserId()) || StringUtils.isBlank(requestParameter.getGoodsId()) ||StringUtils.isBlank(requestParameter.getSource()) || StringUtils.isBlank(requestParameter.getChannel())) {throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());}return router(requestParameter, dynamicContext);}@Overridepublic StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {return switchNode;}}

这里会返回switchNode给AbstractMultiThreadStrategyRouter,此时会执行switchNode的apply方法。

    public R router(T requestParameter, D dynamicContext) throws Exception {StrategyHandler<T, D, R> strategyHandler = get(requestParameter, dynamicContext);if(null != strategyHandler) return strategyHandler.apply(requestParameter, dynamicContext);return defaultStrategyHandler.apply(requestParameter, dynamicContext);}

与上述过程一样,如果switchNode实现了apply中的方法,就会执行,如果没有实现,就不会执行。再次执行doApply后就会再执行router,然后执行switch的get,这里返回了market Node,就会继续往下,以此类推的执行下去,

@Slf4j
@Service
public class SwitchNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> {//业务逻辑return router(requestParameter, dynamicContext);}@Overridepublic StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {return marketNode;}}

MarketNode,在这里我们重写MultiThread方法,使用FutureTask异步查询数据后放入上下文,然后在DoApplay中还可以获取到上下文的数据进行业务处理,处理完毕后在此处还能按照业务进入下一个节点或者返回错误节点。

@Slf4j
@Service
public class MarketNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> {@Resourceprivate ErrorNode errorNode;@Resourceprivate TagNode tagNode;@Overrideprotected void multiThread(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws ExecutionException, InterruptedException, TimeoutException {// 异步查询活动配置// 异步查询商品信息 - 在实际生产中,商品有同步库或者调用接口查询。这里暂时使用DB方式查询。// 写入上下文 - 对于一些复杂场景,获取数据的操作,有时候会在下N个节点获取,这样前置查询数据,可以提高接口响应效率}@Overridepublic TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {// 获取上面查询得到数据的上下文数据// 执行业务,继续放入上下文return router(requestParameter, dynamicContext);}@Overridepublic StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {//走异常节点if (null == dynamicContext.getGroupBuyActivityDiscountVO() || null == dynamicContext.getSkuVO() || null == dynamicContext.getDeductionPrice()) {return errorNode;}return tagNode;}}

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

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

相关文章

GitLab之搭建(Building GitLab)

GitLab之搭建 “ 在企业开发过程中&#xff0c;GitLab凭借其强大的版本管理、CI/CD集成和项目管理功能&#xff0c;成为许多团队的首选工具。本文将探讨GitLab的基础介绍、搭建过程、权限管理、代码审查以及团队知识管理等方面。通过详细的步骤和实用的技巧&#xff0c;旨在帮…

蓝桥杯 小蓝的操作(一维差分)

问题描述 一个数组 aa 中共包含 nn 个数&#xff0c;问最少多少次操作&#xff0c;可以让 aa 数组所有数都变成 11 。 操作的内容是&#xff1a;每次操作可以任选一个区间使得区间内的所有数字减 11 。 数据保证一定有解。 输入格式 第一行一个整数 nn 表示有 nn 个整数。 …

C# net CMS相关开源软件 技术选型 可行性分析

C# net CMS相关开源软件 技术选型 可行性分析 OrchardCMS(微软主导) https://github.com/OrchardCMS/OrchardCore https://docs.orchardcore.net/en/latest/ BSD Umbraco-CMS&#xff08;丹麦&#xff09; https://github.com/umbraco/Umbraco-CMS https://docs.umbraco.com/…

程序化广告行业(77/89):融资、并购与上市全景洞察

程序化广告行业&#xff08;77/89&#xff09;&#xff1a;融资、并购与上市全景洞察 大家好呀&#xff01;一直以来&#xff0c;我都希望能和大家一起在技术知识的海洋里畅游、学习进步。前面我们已经了解了程序化广告行业的发展态势、PC端和移动端投放差异以及行业融资的大致…

【解决方法】VMware 此平台不支持虚拟化Intel VT-x/EPT

目录 1. 引言2. 问题描述3. 解决方法3.1 方法一&#xff08;临时&#xff09;3.2 方法二&#xff08;此方法非常离谱&#xff0c;永久有效&#xff09; 4. &#x1f911;鼓励一下5. 求关注6. 我的其他文章推荐 1. 引言 收集同学们遇到的各种VMware安装、使用过程中遇到的问题&a…

项目学习总结001

1. 策略模式和工厂模式 https://mp.weixin.qq.com/s/RG-h7r69JyKUlBZylJJIFQ 在软件开发中也常常遇到类似的情况&#xff0c;实现某一个功能有多个途径&#xff0c;此时可以使用一种设计模式来使得系统可以灵活地选择解决途径&#xff0c;也能够方便地增加新的解决途径。这就是…

OpenHarmony 5.0版本视频硬件编解码适配

一、简介 Codec HDI&#xff08;Hardware Device Interface&#xff09;对上层媒体服务提供视频编解码的驱动能力接口&#xff0c;主要功能有获取组件编解码能力&#xff0c;创建、销毁编解码器对象&#xff0c;启停编解码器操作&#xff0c;编解码处理等。 Codec HDI 2.0接口…

深度解析基于 Web Search MCP的Deep Research 实现逻辑

写在前面 大型语言模型(LLM)已成为我们获取信息、生成内容的重要工具。但它们的知识大多截止于训练数据的时间点,对于需要实时信息、跨领域知识整合、多角度观点比较的深度研究 (Deep Research) 任务,它们往往力有不逮。如何让 LLM 突破自身知识的局限,像人类研究员一样,…

鸿蒙案例---生肖抽卡

案例源码&#xff1a; Zodiac_cards: 鸿蒙生肖抽奖卡片 效果演示 初始布局 1. Badge 角标组件 此处为语雀内容卡片&#xff0c;点击链接查看&#xff1a;https://www.yuque.com/kevin-nzthp/lvl039/rccg0o4pkp3v6nua 2. Grid 布局 // 定义接口 interface ImageCount {url:…

基于RV1126开发板实现自学习图像分类方案

1. 方案简介 自学习&#xff1a;在识别前对物体图片进行模型学习&#xff0c;训练完成后通过算法分类得出图像的模型ID。 方案设计逻辑流程图&#xff0c;方案代码分为分为两个业务流程&#xff0c;主体代码负责抓取、合成图像&#xff0c;算法代码负责训练和检测功能。 2. 快速…

cat命令查看文件行数

在Linux和Unix-like操作系统中&#xff0c;cat命令主要用于查看文件内容&#xff0c;而不是直接用来查看文件行数。如果你想要查看一个文件的行数&#xff0c;可以使用以下几种方法&#xff1a; 方法1&#xff1a;使用wc命令 wc&#xff08;word count&#xff09;命令可以用…

git清理已经删除的远程分支

目录 命令作用 使用场景 示例流程 注意事项 常见问题 git remote update origin --prune git remote update origin --prune 是一个 Git 命令&#xff0c;用于 更新本地远程跟踪分支 并 清理&#xff08;删除&#xff09;本地已失效的远程分支引用。以下是详细分解&#…

NLP高频面试题(四十)——什么是 BitFit?

BitFit(Bias-term Fine-tuning)是一种参数高效的微调方法,专注于在预训练模型中仅调整偏置项(bias term),而将其他参数保持不变。这种方法在自然语言处理领域,尤其是在中小规模数据集上,展现出了与全量微调相媲美的性能,同时显著减少了计算资源的消耗。 什么是 BitFi…

Java-servlet(完结篇)过滤器乱码解决与监听器

Java-servlet&#xff08;完结篇&#xff09;过滤器乱码解决与监听器 前言一、过滤器乱码解决二、监听器1. HttpSessionListener2. ServletContextListener3. ServletRequestListener 三、监听器的使用场景Java-servlet 结语 前言 在之前的 Java Servlet 学习中&#xff0c;我…

为了避免unboundLocalError和为什么X的值一直不变呢?

## 1.为了避免unboundLocalError 发生unboundLocalError&#xff01; def generate_integer(level):if level 1:X randint(1,9)return X这里出错的原因在于&#xff0c;一旦if 后面的条件没有成立&#xff0c;然后X根本没出生&#xff0c;然后你去使用它&#xff0c;这是有…

opencv-python基础

一.opencv-python简述 其使用Numpy&#xff0c;所有OpenCV数组结构都转换为Numpy数组&#xff0c;是一个高度优化的数据库操作库。 二.环境安装 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-python 三.基本概念 - 像素是图像的基本单元&#xff0c;每个…

ReentrantLock 实现公平锁和非公平锁的原理!

&#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢博主的讲解方式&#xff0c;可以多多支持一下&#xff0c;感谢&#x1f917;&#xff01; &#x1f31f;了解 ThreadLocal请看&#xff1a; ThreadLocal有趣讲解&#xff0c;小白也能听懂&#xff…

NLP高频面试题(四十一)——什么是 IA3 微调?

随着大型语言模型的广泛应用,如何高效地将这些模型适配到特定任务中,成为了研究和工程实践中的重要课题。IA3(Infused Adapter by Adding and Adjusting)微调技术,作为参数高效微调的一种新颖方法,提供了在保持模型性能的同时,显著减少可训练参数数量的解决方案。 IA3 …

swift菜鸟教程14(闭包)

一个朴实无华的目录 今日学习内容&#xff1a;1.Swift 闭包1.1闭包定义1.2闭包实例1.3闭包表达式1.3.1sorted 方法&#xff1a;据您提供的用于排序的闭包函数将已知类型数组中的值进行排序。1.3.2参数名称缩写&#xff1a;直接通过$0,$1,$2来顺序调用闭包的参数。1.3.3运算符函…

蓝桥杯-蓝桥幼儿园(Java-并查集)

并查集的核心思想 并查集主要由两个操作构成&#xff1a; Find&#xff1a;查找某个元素所在集合的根节点。并查集的特点是&#xff0c;每个元素都指向它自己的父节点&#xff0c;根节点的父节点指向它自己。查找过程中可以通过路径压缩来加速后续的查找操作&#xff0c;即将路…