设计模式 之 责任链模式
一搜网上讲责任链的写法都感觉好复杂?我用简单实现让你秒懂并马上用到项目里
前言
搜了一圈责任链模式的文章,要么搬出 UML 类图画半天,要么搞一堆Handler、HandlerChain、AbstractHandler层层嵌套,看得人头大。
今天分享一个我在实际项目中用的责任链实现,总共就 3 个核心文件,代码不超过 80 行,看完直接就能抄到你的项目里。
一、先说场景:什么时候需要责任链?
当你发现代码里有这种结构时:
if(条件A){// 处理逻辑 A}elseif(条件B){// 处理逻辑 B}elseif(条件C){// 处理逻辑 C}else{// 默认处理}每加一个场景就要改这个if-else,越写越长,越改越乱。
责任链就是把这个 if-else 拆成一个个独立的处理节点,串起来依次执行。
二、核心就 3 个东西
| 角色 | 作用 | 对应文件 |
|---|---|---|
| 接口 | 定义"干什么"和"下一个是谁" | ILogicChain |
| 抽象类 | 帮你管好"下一个"的引用 | AbstractLogicChain |
| 实现类 | 每个节点的具体逻辑 | ReBrushChain/ContextCacheChain/DefaultChain |
三、代码逐行拆解
1. 接口 — 定义链的契约
publicinterfaceILogicChain{// 核心方法:处理业务逻辑List<ShardingContext>logic(SalesOrderEnumtype,MageFilterfilter,MetaInfometa);// 获取下一个节点ILogicChainnext();// 追加下一个节点(返回被追加的节点,方便链式调用)ILogicChainappendNext(ILogicChainnext);}关键点:appendNext返回的是ILogicChain而不是void,这样工厂里可以current = current.appendNext(next)一路串下去。
2. 抽象类 — 统一管理 next 引用
@Slf4jpublicabstractclassAbstractLogicChainimplementsILogicChain{privateILogicChainnext;@OverridepublicILogicChainnext(){returnnext;}@OverridepublicILogicChainappendNext(ILogicChainnext){this.next=next;returnnext;// 返回下一个,方便链式组装}// 子类可以标识自己的规则名protectedabstractStringrule();}就这么简单。next 的存取全在抽象类里,子类完全不用关心链的维护。
3. 实现类 — 每个节点只管自己的事
节点 1:反刷链(优先级最高)
@Order(1)publicclassReBrushChainextendsAbstractLogicChain{@OverrideprotectedStringrule(){return"rebrush";}@OverridepublicList<ShardingContext>logic(SalesOrderEnumtype,MageFilterfilter,MetaInfometa){// 命中我的场景 → 我处理,直接返回if(meta.reBrush()){// ... 构建并返回 brushContextListreturnbrushContextList;}// 不命中 → 交给下一个returnnext().logic(type,filter,meta);}}节点 2:上下文缓存链
@Order(2)publicclassContextCacheChainextendsAbstractLogicChain{@OverrideprotectedStringrule(){return"context_cache";}@OverridepublicList<ShardingContext>logic(SalesOrderEnumtype,MageFilterfilter,MetaInfometa){ObjectcontextCacheValue=filter.getValue(CustomProperties.INTEGRATION_BATCH_SCOPE);// 命中 → 从缓存取 Scope,直接返回if(null!=contextCacheValue){// ... 构建并返回 contextListreturncontextList;}// 不命中 → 交给下一个returnnext().logic(type,filter,meta);}}节点 3:默认链(兜底)
@Order(3)publicclassDefaultChainextendsAbstractLogicChain{@OverrideprotectedStringrule(){return"default";}@OverridepublicList<ShardingContext>logic(SalesOrderEnumtype,MageFilterfilter,MetaInfometa){// 兜底节点:直接查数据库拿 ScopeList<ProcessDataScope>scopeList=db.get();returnscopeList;}}注意:DefaultChain 没有调用
next(),因为它是最后一个节点。
4. 工厂 — 把链组装起来
publicclassScopeChainFactory{privatestaticfinalMap<String,ILogicChain>CHAIN_GROUP=newHashMap<>();static{CHAIN_GROUP.put("rebrush",newReBrushChain());CHAIN_GROUP.put("context_cache",newContextCacheChain());CHAIN_GROUP.put("default",newDefaultChain());}// 定义执行顺序privatestaticfinalString[]SCOPE_MODELS=newString[]{"rebrush","context_cache"};publicstaticILogicChainopenChain(){// 拿到头节点ILogicChainlogicChain=CHAIN_GROUP.get(SCOPE_MODELS[0]);ILogicChaincurrent=logicChain;// 依次串起来for(inti=1;i<SCOPE_MODELS.length;i++){ILogicChainnextChain=CHAIN_GROUP.get(SCOPE_MODELS[i]);current=current.appendNext(nextChain);}// default 兜底,确保链不会断current.appendNext(CHAIN_GROUP.get("default"));returnlogicChain;}}四、调用方怎么用?
一行代码:
List<ShardingContext>result=ScopeChainFactory.openChain().logic(salesType,filter,metaInfo);调用方完全不用关心内部走了哪个节点,链会自己判断。
五、执行流程图
调用入口 ↓ ReBrushChain (Order=1) ├─ 命中反刷场景? → 处理并返回 └─ 没命中 → next() ↓ ContextCacheChain (Order=2) ├─ 有上下文缓存? → 处理并返回 └─ 没有缓存 → next() ↓ DefaultChain (Order=3, 兜底) └─ 查数据库拿 Scope → 处理并返回六、和网上的写法比,简单在哪?
| 对比项 | 网上的写法 | 本文写法 |
|---|---|---|
| 类的数量 | 5-8 个(Handler、Chain、Builder、Context…) | 3 个(接口、抽象类、实现类) |
| 组装方式 | Builder 嵌套 / Spring 注入 List 再排序 | 静态 Map + 数组定义顺序 |
| 传递方式 | 封装一个 Context 对象层层传递 | 方法参数直接传,清晰明了 |
| 终止条件 | 各种 boolean 返回值判断 | 命中就 return,没命中就next() |
七、要加新节点怎么办?
3 步:
- 新建实现类,继承
AbstractLogicChain,实现logic()方法 - 在
ScopeChainFactory的CHAIN_GROUP里注册 - 在
SCOPE_MODELS数组里加上它的位置
完事。不用改任何已有代码,符合开闭原则。
总结
责任链模式的本质就是:把 if-else 变成一串独立节点,每个节点自己决定"我处理"还是"交给下一个"。
不需要复杂的框架,不需要一堆辅助类。一个接口、一个抽象类、几个实现类、一个工厂,就够了。
直接抄到你的项目里,把logic()方法的参数换成你自己的业务参数,马上就能用。
