当前位置: 首页 > news >正文

责任链与规则树设计实战解析(自用)

前言在自学的过程中,了解到责任链和规则树的设计,由于本人完全不会这些东西,即使写了代码,也看得我一脸懵逼,不清楚代码是怎么跑的,节点的流转是怎么实现的?所以就根据现有的代码和资料进行分析,然后就有了这篇文章。希望这篇文章帮助我自己梳理思路的同时能给到你帮助!

如果你需要更全地了解责任链与规则树设计的内容,可以看看下面这位博主的文章:

小傅哥-干掉if...else,最好用的3种设计模式!https://bugstack.cn/md/develop/design-pattern/2024-08-25-chain-tree.html

一,案例背景

项目的背景是一个拼团营销系统,本次任务的主要业务是需要在流程中查询 拼团活动配置 ,查询 拼团商品信息,为了使后续的代码更便于维护扩展,于是添加一个 tree规则树抽象模型,通过解耦逻辑和划分功能区,让代码具有了文档属性,看到对应的类和类下的方法区,就可以轻松的理解代码实现方式。

二,规则树-代码控制 模型设计

模型设计分析如下图:

1. 核心角色:“找节点” 和 “做事情”

  • StrategyMapper(策略映射器):负责 “找下一个该执行的节点”,通过 get() 方法完成。

  • StrategyHandler(策略处理器):负责 “执行当前节点的业务逻辑”,通过 apply() 方法完成。

2. 路由模板:AbstractStrategyRouter(抽象策略路由处理器)

它是 “找节点 + 做事情 + 传递流程” 的通用模板

  • 实现了 StrategyMapper 和 StrategyHandler(所以既会 “找节点”,也会 “做事情”)。
  • 额外有 router() 方法:负责 “把流程传递给下层节点”(比如当前节点执行完,调用下一个节点的业务)。

3. 业务专属扩展:AbstractGroupBuyMarketSupport(抽象拼团营销支撑类)

继承自 AbstractStrategyRouter,所以带有 “找节点、做事情、传流程” 的能力,但专门为 “拼团业务” 定制:

  • 内部带有 “拼团业务专属的配置”(比如设置线程池配置,注入仓储依赖等)。

4. 流程起点工厂:DefaultActivityStrategyFactory(默认活动策略工厂)

负责 “生产流程的起点(根节点)”,通过 strategyHandler() 方法获取 RootNode

5. 流程节点链(责任链模式)

这些节点AbstractStrategyRouterrouter()方法控制节点的流转,每个节点都继承自 AbstractGroupBuyMarketSupport,都有 apply()(做自己的事)和 get()(找下一个节点)方法:

  • RootNode(根节点):流程入口与参数校验。

  • SwitchNode(开关节点):分支判断与流量管控。。

  • MarketNode(营销节点):核心营销逻辑与异步数据聚合。

  • EndNode(结束节点):结果封装与流程收尾。

三,本案例业务中的流程

编写一个测试方法去查询活动业务中的数据,活动业务service中的实现类便会去调用 DefaultActivityStrategyFactor 类获取到Root节点通过Root节点调用 apply() 方法进入节点链,在节点链中由AbstractStrategyRouter 的 router() 方法进行节点的流行控制,router() 方法调用策略映射器 get() 方法获取节点,得到对应的节点后调用节点的 apply() 方法去执行业务,然后返回。(通过继承关系,每个节点都带有策略处理器的apply方法)。

在获取到的节点(如Switch节点)中执行自己的业务(参数校验)完成后 再调用 router() 方法返回。

这样又进入到了AbstractStrategyRouter 中的 router() 方法中,又继续调用 get() 方法获取到下一个节点(如Marker节点),以此类推。

 AbstractSrategyRouter.class

public abstract class AbstractStrategyRouter<T, D, R> implements StrategyHandler<T, D, R>, StrategyMapper<T, D, R> {@Getter@Setterprotected StrategyHandler<T,D,R> defaultStrategyHandler = StrategyHandler.DEFAULT;public R router(T requestParameter, D dynamicContext) throws Exception {//通过调用策略映射器get方法,控制节点流程的走向。StrategyHandler<T, D, R> strategyHandler = get(requestParameter, dynamicContext);if (null != strategyHandler) {return strategyHandler.apply(requestParameter, dynamicContext);}return defaultStrategyHandler.apply(requestParameter, dynamicContext);}}

问题为什么每次调用 get() 方法就能获取到下一个需要的节点呢?

补充DefaultActivityStrategyFactory 类中注入了根节点RootNode,且提供了strategyHandler() 方法 返回RootNode。比如在拼团活动service业务中,会先调用 DefaultActivityStrategyFactory strategyHandler() 方法来获取一个 StrategyHandler 的对象,再通过这个对象 strategyHandler.apply() 去执行自己的业务。(因为这里的strategyHandler已经被赋值为RootNode,所以就是执行根节点的业务)

后续进入AbstractStrategyRouter 中的 router() 方法调用 get() 能获取到 需要的下一个节点是因为每一个节点都重写了 get() 方法,在方法中指定了自己的下一个节点是谁。

拼团活动业务中的service实现类参考:

@Service
public class IIndexGroupBuyMarketServiceImpl implements IIndexGroupBuyMarketService {@Resourceprivate DefaultActivityStrategyFactory defaultActivityStrategyFactory;@Overridepublic TrialBalanceEntity indexMarketTrial(MarketProductEntity marketProductEntity) throws Exception {StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> strategyHandler = defaultActivityStrategyFactory.strategyHandler();TrialBalanceEntity trialBalanceEntity = strategyHandler.apply(marketProductEntity, new DefaultActivityStrategyFactory.DynamicContext());return trialBalanceEntity;}}

RootNode节点代码参考:

@Service
public class RootNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> {@Resourceprivate SwitchRoot switchRoot;@Overridepublic TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {// 参数判断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);}//重写StrategyMapper的get方法@Overridepublic StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) {return switchRoot;}
}

四、结尾

目前还在学习,先做个阶段性分析总结帮助自己理解。

http://www.dtcms.com/a/435721.html

相关文章:

  • 网站建设太金手指六六二九代理产品网
  • 栾城做网站云南信息发布平台
  • DrissionPage爬取汽车之家:(车名 + 颜色 + 车辆型号/续航里程)
  • 360做企业网站多少钱淘宝客网站怎么做的
  • 网站制作课程介绍清远网站制作公司
  • 宁志网站两学一做匀贵网站建设
  • 树莓派点亮LED灯
  • 如何解决 pip install -r requirements.txt 安装报错 递归包含:文件通过 -r 引用了自身 问题
  • 【实证分析】A股上市公司权益资本成本数据-基于MPEG模型(2001-2024年)
  • 制作公司工作网站网站数据库是干什么的
  • 亚马逊网站建设目的wordpress证优客
  • 科技信息差(10.2)
  • 设计网站的目的重庆九龙坡营销型网站建设公司推荐
  • 建设ftp网站的安全性网站建设模板可用吗
  • 优秀网站作品做网站cpa
  • 网站 被刷流量东营网约车最新消息
  • 电路中的 ”CT“
  • 自建网站和第三方平台自己做众筹网站
  • 网站做流量是怎么回事邢台网站建设有哪些
  • 文本引导的图像融合方法
  • 免费空间访客100个网站桓台网站建设公司
  • 网站防盗链怎么做制作html网页的详细步骤
  • 网站建设微享互动桂林市区面积
  • 怎么看网站是否备案成功建筑木工招聘平台
  • 合肥网站制作哪家有名网站建设的步骤教程视频
  • 天博网站建设如何做网页宣传
  • 北京移动端网站公司中国蔬菜网网站建设电话
  • 怎么解决360导航的网站建设合肥做双语网站
  • 莱芜网站优化方案河南省建设厅网站师林峰
  • 泰山派rk3566 linux sdk 修改内核增加模块