Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
摘要:责任链模式将请求的发送者与接收者解耦,让多个对象都有机会处理请求,通过将对象串成一条链,请求沿链自动传递,直到被某个对象处理。本文结合物流公司请假审批系统的场景,完整展示如何用责任链模式实现分级审批,支持动态调整审批链,并与命令模式深度对比,帮你掌握“请求传递,各司其职”的设计精髓。
🗺️本文阅读地图(3 分钟速览)
- 为什么审批流程的 if-else 一定会炸?
- ✅ 责任链核心角色:抽象处理者、具体处理者
- 手写请假审批链:组长→经理→HR,自动传递
- 🚀 动态调整链:临时跳过某一级审批
- 🎤 面试必问:“责任链模式和命令模式有什么区别?”
📖《Java 23 种设计模式:从踩坑到精通》
开篇:系列介绍与目录 | 正篇:责任链模式 Chain of Responsibility —— 请求流转,审批流程的本质 |当前:番外 · 责任链模式 × 物流审批流程
🔗 返回系列总目录
1. 物流审批流程的痛点
在物流公司,请假审批需要根据不同天数由不同级别审批:3 天以内由直属领导审批,3 到 7 天由部门经理审批,7 到 30 天由 HR 总监审批,超过 30 天直接拒绝。如果把这个逻辑写成if-else:
if(days<=3){teamLeader.approve();}elseif(days<=7){manager.approve();}elseif(days<=30){hr.approve();}else{reject();}这种写法将审批规则死死绑在代码中。当审批流程变化(如增加财务审批、临时授权某人越级审批)时,必须修改核心代码。更糟糕的是,如果审批节点需要动态调整(如经理出差时临时跳过),静态if-else完全无法应对。
责任链模式的解决思路:将各个审批节点串联成一条链,请求从链头开始自动传递,每个节点只判断“我能不能处理”,能处理则处理,不能处理则传给下一个节点。链的结构可在运行时动态调整,完全符合开闭原则。
1.1 你的场景该不该用责任链?
| 判断标准 | 是 → 用责任链 | 否 → 用其他方式 |
|---|---|---|
| 有多个对象可以处理同一请求,处理器动态确定 | ✅ | ❌ |
| 处理器顺序需要灵活调整,甚至运行时动态改变 | ✅ | ❌ |
| 希望请求自动传递,无需客户端指定由谁处理 | ✅ | ❌ |
| 处理逻辑非常简单,且处理器固定不变 | ❌ | 直接调用即可 |
2. 责任链模式 UML(物流请假审批场景)
3. 完整源码实现
3.1 请求对象:请假申请 (LeaveRequest)
/** * 请假请求对象:封装请求数据 */publicclassLeaveRequest{privateStringemployeeName;privateintdays;privateStringreason;publicLeaveRequest(StringemployeeName,intdays,Stringreason){this.employeeName=employeeName;this.days=days;this.reason=reason;}publicStringgetEmployeeName(){returnemployeeName;}publicintgetDays(){returndays;}publicStringgetReason(){returnreason;}@OverridepublicStringtoString(){returnString.format("%s申请请假%d天,原因:%s",employeeName,days,reason);}}💬白话:请假请求就像一个“工单”,上面写明了谁请假、请几天、什么原因。它不关心谁来审批,只管带着数据在链上跑。
3.2 抽象处理者:审批者 (Approver)
/** * 抽象审批者:定义处理请求的统一接口 */publicabstractclassApprover{protectedApprovernextApprover;protectedStringname;publicApprover(Stringname){this.name=name;}/** * 设置下一个审批者,支持链式调用 */publicApproversetNext(Approvernext){this.nextApprover=next;returnnext;}/** * 处理请求 */publicvoidhandleRequest(LeaveRequestrequest){if(canApprove(request.getDays())){approve(request);}elseif(nextApprover!=null){System.out.printf(" ⏩ %s 无权审批%d天,转交给 %s\n",name,request.getDays(),nextApprover.name);nextApprover.handleRequest(request);}else{reject(request);}}protectedabstractbooleancanApprove(intdays);protectedvoidapprove(LeaveRequestrequest){System.out.printf(" ✅ %s 批准了:%s\n",name,request);}protectedvoidreject(LeaveRequestrequest){System.out.printf(" ❌ %s 拒绝了:%s(超出最大审批权限)\n",name,request);}}💬白话:
handleRequest是责任链的核心——先看自己能不能批,能就批;不能就甩给下一个。setNext返回下一个节点,支持链式拼接,像“链表”一样把审批者串起来。
3.3 具体处理者:直属领导 (TeamLeader)
publicclassTeamLeaderextendsApprover{publicTeamLeader(Stringname){super(name);}@OverrideprotectedbooleancanApprove(intdays){returndays<=3;}}3.4 具体处理者:部门经理 (DepartmentManager)
publicclassDepartmentManagerextendsApprover{publicDepartmentManager(Stringname){super(name);}@OverrideprotectedbooleancanApprove(intdays){returndays<=7;}}3.5 具体处理者:HR 总监 (HRDirector)
publicclassHRDirectorextendsApprover{publicHRDirector(Stringname){super(name);}@OverrideprotectedbooleancanApprove(intdays){returndays<=30;}}3.6 客户端测试
publicclassClient{publicstaticvoidmain(String[]args){System.out.println("=== 物流公司请假审批系统(责任链模式)===\n");// 1. 构建责任链:直属领导 → 部门经理 → HR总监ApproverteamLeader=newTeamLeader("张组长");Approvermanager=newDepartmentManager("李经理");Approverhr=newHRDirector("王总监");teamLeader.setNext(manager).setNext(hr);// 链式拼接// 2. 测试不同天数LeaveRequest[]requests={newLeaveRequest("小赵",1,"感冒发烧"),newLeaveRequest("小钱",5,"回家探亲"),newLeaveRequest("小孙",15,"婚假"),newLeaveRequest("小李",45,"长期进修")};for(LeaveRequestrequest:requests){System.out.println("【申请】"+request);teamLeader.handleRequest(request);System.out.println();}// 3. 动态调整:经理出差,跳过经理直接到 HRSystem.out.println("=== 临时授权:跳过经理 ===");Approvertl=newTeamLeader("张组长");tl.setNext(newHRDirector("王总监"));tl.handleRequest(newLeaveRequest("小周",6,"家庭事务"));}}💬白话:
setNext(manager).setNext(hr)是链式拼接的标准写法。场景 3 中,teamLeader直接连到hr,跳过了经理——这就是责任链在运行时的灵活性。
4. 运行结果
=== 物流公司请假审批系统(责任链模式)=== 【申请】小赵申请请假1天,原因:感冒发烧 ✅ 张组长 批准了:小赵申请请假1天,原因:感冒发烧 【申请】小钱申请请假5天,原因:回家探亲 ⏩ 张组长 无权审批5天,转交给 李经理 ✅ 李经理 批准了:小钱申请请假5天,原因:回家探亲 【申请】小孙申请请假15天,原因:婚假 ⏩ 张组长 无权审批15天,转交给 李经理 ⏩ 李经理 无权审批15天,转交给 王总监 ✅ 王总监 批准了:小孙申请请假15天,原因:婚假 【申请】小李申请请假45天,原因:长期进修 ⏩ 张组长 无权审批45天,转交给 李经理 ⏩ 李经理 无权审批45天,转交给 王总监 ❌ 王总监 拒绝了:小李申请请假45天,原因:长期进修(超出最大审批权限) === 临时授权:跳过经理 === ⏩ 张组长 无权审批6天,转交给 王总监 ✅ 王总监 批准了:小周申请请假6天,原因:家庭事务5. 核心角色回顾
| 角色 | 职责 | 对应代码 |
|---|---|---|
| Handler | 定义处理请求的接口,持有下一个处理者引用 | Approver |
| ConcreteHandler | 处理自己能处理的请求,否则传给下一个 | TeamLeader/DepartmentManager/HRDirector |
6. 责任链模式 vs 命令模式
| 对比项 | 责任链模式 | 命令模式 |
|---|---|---|
| 意图 | 请求沿链传递,直到被处理 | 将请求封装为对象,支持撤销/队列 |
| 请求与处理者关系 | 一个请求可被多个处理者“过目” | 一个命令对应一个接收者 |
| 典型应用 | 审批流程、Filter 链 | 遥控器、宏命令、线程池 |
💡一句话记忆:责任链是“传话接力”——谁能处理谁就留下;命令模式是“指令封装”——执行者拿到指令直接干活,不会传给下一个人。
7. 责任链模式的优缺点
| 优点 | 缺点 |
|---|---|
| 请求发送者与处理者解耦,灵活扩展 | 链太长时性能有影响 |
| 可动态调整链的结构 | 调试不便,需追踪整条链 |
| 符合单一职责和开闭原则 | 请求可能到达链尾仍未被处理 |
8. 六大设计原则体现
| 原则 | 体现 |
|---|---|
| 单一职责 | 每个审批者只负责自己权限范围内的审批 |
| 开闭原则 | 新增审批级别只需加新类,不改原有代码 |
| 里氏替换 | 所有审批者都可替换抽象Approver |
| 依赖倒置 | 客户端依赖抽象Approver,不依赖具体审批者 |
| 接口隔离 | Approver方法精简,只定义处理与设置下一个 |
| 迪米特法则 | 请求只与链头交互,不知道链的结构 |
附 责任链模式 UML 源码(物流请假审批场景)
@startuml title Java 23 种设计模式:从踩坑到精通 footer 折哥 | 智能物流与Java实战 ' 1. 全局样式配置 skinparam backgroundColor #FEFEFE skinparam shadowing false skinparam classBorderColor #333333 skinparam classFontColor #1A1A1A skinparam classFontSize 14 skinparam noteFontSize 12 skinparam noteFontColor #555555 skinparam arrowColor #555555 skinparam classBackgroundColor #F9F9F9 skinparam interface { BackgroundColor #E8F5E9 BorderColor #2E7D32 } ' 2. 请求对象 class LeaveRequest { - employeeName : String - days : int - reason : String + getDays() : int + getEmployeeName() : String } note right of LeaveRequest <b>请假请求</b> -- 封装请求数据 包含员工姓名、天数、原因 end note ' 3. 抽象处理者 abstract class Approver { - nextApprover : Approver - name : String + handleRequest(request : LeaveRequest) : void + setNext(next : Approver) : Approver # canApprove(days : int) : boolean } note right of Approver <b>抽象处理者</b> -- 定义处理请求的接口 持有下一个处理者的引用 子类实现具体的审批逻辑 end note ' 4. 具体处理者:直属领导 class TeamLeader extends Approver { + handleRequest(request : LeaveRequest) : void # canApprove(days : int) : boolean } note right of TeamLeader <b>直属领导</b> -- 审批权限:≤ 3天 超过3天则转交给上级 审批通过后流程结束 end note ' 5. 具体处理者:部门经理 class DepartmentManager extends Approver { + handleRequest(request : LeaveRequest) : void # canApprove(days : int) : boolean } note right of DepartmentManager <b>部门经理</b> -- 审批权限:≤ 7天 超过7天则转交给HR 审批通过后流程结束 end note ' 6. 具体处理者:HR class HRDirector extends Approver { + handleRequest(request : LeaveRequest) : void # canApprove(days : int) : boolean } note bottom of HRDirector <b>HR总监</b> -- 审批权限:≤ 30天 作为最终兜底处理者 超过30天则拒绝 end note ' 7. 关系连线 LeaveRequest <-- Approver : 处理 Approver o--> Approver : next 指向下一个 TeamLeader --|> Approver : 继承 DepartmentManager --|> Approver : 继承 HRDirector --|> Approver : 继承 @enduml🧭 《Java 23 种设计模式:从踩坑到精通》快速导航
- 开篇:系列介绍与目录
- 正篇:责任链模式 Chain of Responsibility —— 请求流转,审批流程的本质
- 当前:番外 · 责任链模式 × 物流审批流程(你在这里)
- 创建型模式汇总
- 结构型模式汇总
- 行为型模式汇总
🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。
📌 除了设计模式,我也在深挖智能物流实战(WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》、《电商多平台电子面单对接实战》。技术相通,思路可鉴。
