责任链模式:让每个节点都有机会处理请求

责任链模式,说白了就是一条处理流水线。请求从链头进入,沿着链条依次传递,直到某个节点把它处理掉。我最早接触这个模式是在做审批系统的时候——一个报销单要经过组长、经理、总监、CEO,每个人根据自己的权限决定是批还是往上扔。

嗯,这个模式的核心价值就一句话:解耦请求的发送者和接收者。发送者不需要知道谁会处理,接收者也不需要知道请求从哪来。

模式结构

责任链模式由三个角色组成:

  • Handler(抽象处理者):定义处理请求的接口,持有下一个处理者的引用
  • ConcreteHandler(具体处理者):实现处理逻辑,能处理就处理,不能处理就传给下一个
  • Client(客户端):构建链条,发起请求

我习惯把责任链想象成一根竹竿,每个节点就是竹竿上的一节。请求像水一样流过,遇到能吸收的节点就停下来。

核心要点:每个处理者只关心自己能不能处理,不关心链条上有谁、后面是谁。这就是所谓的「单一职责」在链条上的体现。

代码示例:审批流程

来看一个实际例子。假设公司报销审批规则是这样的:

  • 1000元以下:组长审批
  • 1000-5000元:经理审批
  • 5000-20000元:总监审批
  • 20000元以上:CEO审批

用责任链模式实现:

// 抽象处理者
abstract class Approver {
    protected Approver next;
    
    public void setNext(Approver next) {
        this.next = next;
    }
    
    public abstract void handleRequest(int amount);
}

// 组长
class TeamLeader extends Approver {
    public void handleRequest(int amount) {
        if (amount <= 1000) {
            System.out.println("组长审批通过:" + amount);
        } else if (next != null) {
            next.handleRequest(amount);
        }
    }
}

// 经理
class Manager extends Approver {
    public void handleRequest(int amount) {
        if (amount <= 5000) {
            System.out.println("经理审批通过:" + amount);
        } else if (next != null) {
            next.handleRequest(amount);
        }
    }
}

// 总监
class Director extends Approver {
    public void handleRequest(int amount) {
        if (amount <= 20000) {
            System.out.println("总监审批通过:" + amount);
        } else if (next != null) {
            next.handleRequest(amount);
        }
    }
}

// CEO
class CEO extends Approver {
    public void handleRequest(int amount) {
        System.out.println("CEO审批通过:" + amount);
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        Approver teamLeader = new TeamLeader();
        Approver manager = new Manager();
        Approver director = new Director();
        Approver ceo = new CEO();
        
        // 构建链条
        teamLeader.setNext(manager);
        manager.setNext(director);
        director.setNext(ceo);
        
        // 发起请求
        teamLeader.handleRequest(800);    // 组长处理
        teamLeader.handleRequest(3000);   // 经理处理
        teamLeader.handleRequest(15000);  // 总监处理
        teamLeader.handleRequest(50000);  // CEO处理
    }
}

你看,客户端只需要把请求扔给链条的第一个节点,后面的事它完全不用管。这就是解耦。

责任链的两种变体

我在项目中遇到过两种常见的责任链实现方式:

类型 特点 适用场景
纯责任链 一个请求最多被一个节点处理 审批流程、权限校验
不纯责任链 一个请求可以被多个节点处理 日志记录、过滤器链

纯责任链就像我刚才写的审批例子——请求被某个节点处理后就不再往下传了。不纯责任链则相反,每个节点处理完还会继续传递,比如日志系统里,debug日志要经过控制台输出、文件输出、远程输出等多个处理器。

SVG:责任链模式结构图

责任链模式结构图 客户端 (Client) 处理者1 (Handler1) 处理者2 (Handler2) 处理者3 (Handler3) 请求流向 → 每个处理者持有下一个处理者的引用,形成链式结构

实际应用场景

责任链模式在真实项目中随处可见。我列几个常见的:

  • Web框架的过滤器链:比如Java Servlet中的Filter,每个过滤器处理完请求后调用chain.doFilter()传给下一个
  • 日志框架:Log4j、Logback中的Logger,根据日志级别决定是否输出到不同目的地
  • 异常处理:try-catch块本身就是一种责任链,异常从最内层catch逐层向外传递
  • 游戏中的碰撞检测:游戏对象按优先级组成链条,检测到碰撞就处理,否则传给下一个

我的建议:当你发现代码中出现大量的if-else判断「如果A能处理就A处理,否则如果B能处理就B处理...」时,就该考虑用责任链模式重构了。这种if-else链条越长,维护成本越高。

避坑指南

我曾经踩过的坑:有一次我在一个支付系统中用了责任链模式来处理订单状态变更。链条上有10个节点,结果某个节点忘记调用next.handleRequest(),导致后面的节点全部失效。排查了整整一个下午。

所以,一定要确保每个节点要么处理请求并终止,要么明确地传给下一个。不要出现「既没处理也没传递」的情况。

另外还有一个坑:链条太长会导致性能问题。你想想看,如果一个请求要经过20个节点才被处理,每次请求都要遍历20次。我曾经在一个项目中看到有人把责任链用成了链表遍历,每个节点只做一点点事,结果整个链条有30多个节点。后来我建议他把多个小节点合并成一个大节点,性能提升了40%。

与装饰器模式的区别

很多人容易把责任链模式和装饰器模式搞混。我简单说一下区别:

  • 责任链模式:请求沿着链条传递,每个节点可以选择处理或跳过。重点是「传递」
  • 装饰器模式:每个装饰器对核心对象进行增强,所有装饰器都会生效。重点是「叠加」

举个例子:责任链就像安检通道,每个安检员检查一项,不合格就拦下。装饰器就像给手机贴膜、加壳、挂绳,每个装饰都加上去,最终得到一个全副武装的手机。

总结

责任链模式是一种优雅的解耦方案。它让请求的发送者和接收者不再直接耦合,而是通过一条链来传递。每个节点只关心自己的职责,不需要知道链条的全貌。

我个人习惯在以下场景优先考虑责任链:

  • 有多个对象可以处理同一个请求,但具体由谁处理在运行时才能确定
  • 需要动态指定处理者的集合和顺序
  • 不想让发送者知道具体哪个接收者会处理请求

嗯,责任链模式就讲到这里。记住一句话:让请求在链条上流动,让每个节点只做自己该做的事


公众号:蓝海资料掘金营,微信deep3321