前言:我在自学的过程中,了解到责任链和规则树的设计,由于完全不会这些东西,即使写了代码,也看得我一脸懵逼,不清楚代码是怎么跑的,节点的流转是怎么实现的?所以就根据现有的代码和资料进行分析,然后就有了这篇文章。希望这篇文章在帮助我梳理思路的同时能给到你帮助!
如果你需要更全地了解责任链与规则树设计的内容,可以看看下面这位博主的文章:
小傅哥-干掉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(结束节点):结果封装与流程收尾。
三,本案例业务的流程
在案例中,前面的节点已经实现了对数据的校验,而此次的主要内容就是在营销节点里查询“活动配置”和“商品信息”。我们在营销节点的 apply()方法 中实现上面提到的业务内容,

1,业务流程走向分析:
我们在测试类中编写一个测试方法去调用活动业务的方法(这里是indexMarketTrial),在业务方法中去调用 DefaultActivityStrategyFactor 类获取到 Root节点,通过获取到的 Root节点 调用 apply() 方法进入节点链,在节点链中由 AbstractStrategyRouter 的 router() 方法进行节点的流转控制,router() 方法中调用策略映射器 get() 方法获取下一个节点,得到下一个节点后调用下一个节点的 apply() 方法去执行业务,然后返回。(由于继承关系,每个节点要重写策略处理器的apply方法)。
拼团活动业务中的service实现类参考:
@Service public class IIndexGroupBuyMarketServiceImpl implements IIndexGroupBuyMarketService { @Resource private DefaultActivityStrategyFactory defaultActivityStrategyFactory; @Override public TrialBalanceEntity indexMarketTrial(MarketProductEntity marketProductEntity) throws Exception { StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> strategyHandler = defaultActivityStrategyFactory.strategyHandler(); TrialBalanceEntity trialBalanceEntity = strategyHandler.apply(marketProductEntity, new DefaultActivityStrategyFactory.DynamicContext()); return trialBalanceEntity; } }
2,问题:为什么每次调用 get() 方法就能获取到下一个需要的节点呢?(How节点流转)
补充:在 DefaultActivityStrategyFactory 类中注入了根节点RootNode,且提供了strategyHandler() 方法 返回RootNode。比如在上面的拼团活动service实现类中,会先调用 DefaultActivityStrategyFactory 的 strategyHandler() 方法来获取一个 StrategyHandler 的对象,再通过这个对象 strategyHandler.apply() 去执行自己的业务。(因为这里的strategyHandler已经被赋值为RootNode,所以可以看作RootNode.apply() )
后续进入AbstractStrategyRouter 中的 router() 方法调用 get() 能获取到 需要的下一个节点是因为每一个节点都重写了 get() 方法,在节点get()方法中指定了自己的下一个节点是谁。
下面是 AbstractSrategyRouter 和 RootNode 的代码参考
AbstractSrategyRouter.class
public abstract class AbstractStrategyRouter<T, D, R> implements StrategyHandler<T, D, R>, StrategyMapper<T, D, R> { @Getter @Setter protected 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) { //使用获取到的节点调用apply方法执行业务 return strategyHandler.apply(requestParameter, dynamicContext); } //如果获取到了null,则调用"默认策略处理器"的apply()方法(即直接return null) return defaultStrategyHandler.apply(requestParameter, dynamicContext); } }
RootNode.class:@Service public class RootNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> { //*MarketProductEntity-入参类型;DefaultActivityStrategyFactory.DynamicContext-动态上下文,TrialBalanceEntity-出参类型 @Resource private SwitchRoot switchRoot; @Override public TrialBalanceEntity apply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception { // 参数判断 if (StringUtils.isBlank(requestParameter.getUserId()) || 其他判断...) { throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo()); } return router(requestParameter, dynamicContext); } //重写StrategyMapper的get方法 @Override public StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) { return switchRoot; } }其他节点和RootNode节点一样都是重写 apply() 和 get(),区别只是方法内实现的内容不同。
四、结尾
目前还在学习,先做个阶段性总结帮助自己理解,后续在学习中可能会继续补充修正!
1318

被折叠的 条评论
为什么被折叠?



