跳到主要内容

责任链模式详解

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. 总结

  • 责任链模式适合“多节点可插拔”的流程编排。
  • 通过清晰的抽象类和统一入口,既能保持代码整洁,也方便后续扩展。
  • 掌握这个审批示例后,再面对业务中更复杂的链路(如数据处理、过滤器、权限校验等),理解成本会大幅降低。