责任链与规则树设计实战解析(自用)
前言:在自学的过程中,了解到责任链和规则树的设计,由于本人完全不会这些东西,即使写了代码,也看得我一脸懵逼,不清楚代码是怎么跑的,节点的流转是怎么实现的?所以就根据现有的代码和资料进行分析,然后就有了这篇文章。希望这篇文章帮助我自己梳理思路的同时能给到你帮助!
如果你需要更全地了解责任链与规则树设计的内容,可以看看下面这位博主的文章:
小傅哥-干掉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. 流程节点链(责任链模式)
这些节点AbstractStrategyRouter的router()方法控制节点的流转
,每个节点都继承自 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;} }
四、结尾
目前还在学习,先做个阶段性分析总结帮助自己理解。