责任链模式详解
1. 为什么需要责任链模式
- 很多业务需要“多步骤顺序处理”,但具体由谁处理往往视情况而定,硬编码多个 if/else 会让代码臃肿。
- 责任链模式(Chain of Responsibility)把多个处理步骤串成链;每个节点只决定“能不能处理”和“是否继续传递”,请求发送者不必指定处理者,扩展也更灵活。
2. 场景设定:差旅报销审批
设定一条简单流程:
- 组长审批:金额 ≤ 1,000。
- 经理审批:金额 ≤ 5,000。
- 总监审批:金额 > 5,000,由总监兜底。
链路结构可以想象成:Leader → Manager → Director
;请求会从链头进入,逐个节点判断并处理。
3. 代码实现步骤
3.1 抽象处理者
- 提供
setNext
串联下一个处理者。 handle
模板方法:先判断是否能处理,能则执行业务,否则给下家;链末尾仍没人处理则打印提示。
public abstract class ExpenseHandler {
private ExpenseHandler next;
public ExpenseHandler setNext(ExpenseHandler next) {
this.next = next;
return next;
}
public final void handle(ExpenseRequest request) {
if (canHandle(request)) {
doHandle(request);
} else if (next != null) {
next.handle(request);
} else {
System.out.println("无人可处理:" + request);
}
}
protected abstract boolean canHandle(ExpenseRequest request);
protected abstract void doHandle(ExpenseRequest request);
}
3.2 请求数据对象
- 使用
record
简化数据载体,也可换成常规 POJO。
public record ExpenseRequest(String applicant, double amount) {}
3.3 具体处理者
- 每个处理者继承抽象类,明确自身责任范围。
canHandle
决定是否处理;doHandle
是真正的业务逻辑。
public class LeaderHandler extends ExpenseHandler {
@Override
protected boolean canHandle(ExpenseRequest request) {
return request.amount() <= 1_000;
}
@Override
protected void doHandle(ExpenseRequest request) {
System.out.println("组长审批通过:" + request);
}
}
public class ManagerHandler extends ExpenseHandler {
@Override
protected boolean canHandle(ExpenseRequest request) {
return request.amount() <= 5_000;
}
@Override
protected void doHandle(ExpenseRequest request) {
System.out.println("经理审批通过:" + request);
}
}
public class DirectorHandler extends ExpenseHandler {
@Override
protected boolean canHandle(ExpenseRequest request) {
return true; // 总监兜底
}
@Override
protected void doHandle(ExpenseRequest request) {
System.out.println("总监审批通过:" + request);
}
}
3.4 构建链路并运行
public class ExpenseClient {
public static void main(String[] args) {
ExpenseHandler leader = new LeaderHandler();
ExpenseHandler manager = new ManagerHandler();
ExpenseHandler director = new DirectorHandler();
leader.setNext(manager).setNext(director);
leader.handle(new ExpenseRequest("Alice", 800));
leader.handle(new ExpenseRequest("Bob", 3_000));
leader.handle(new ExpenseRequest("Charlie", 8_000));
}
}
运行结果
组长审批通过:ExpenseRequest[applicant=Alice, amount=800.0]
经理审批通过:ExpenseRequest[applicant=Bob, amount=3000.0]
总监审批通过:ExpenseRequest[applicant=Charlie, amount=8000.0]
4. 核心要点解析
- 单一职责:每个节点只关注自己的判断和处理逻辑。
- 可扩展性:新增审批角色(如财务复核)只需实现新的处理者,插入链路即可。
- 控制流:通过
setNext
手动控制顺序;实际项目中可以结合配置或注解自动组装。 - 兜底策略:责任链必须考虑无人处理的情况,通常在链尾安排“默认处理者”或抛异常。
5. 变体与优化提示
- 中断链路:如果某个节点处理完就不需要继续,可以在
doHandle
内明确不再调用next
,或在canHandle
中返回false
。 - 返回值:可以把
handle
改成返回结果对象或状态码,满足需要回传信息的场景。 - 异步/并行:对于非强顺序处理,可结合事件驱动或异步队列实现更复杂的责任链。
- 调试监控:可在抽象类中加入日志埋点,记录每个节点的进入与退出,便于排查问题。
6. 总结
- 责任链模式适合“多节点可插拔”的流程编排。
- 通过清晰的抽象类和统一入口,既能保持代码整洁,也方便后续扩展。
- 掌握这个审批示例后,再面对业务中更复杂的链路(如数据处理、过滤器、权限校验等),理解成本会大幅降低。