什么是责任链模式?应用场景有哪些?

一则或许对你有用的小广告

欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于 Spring AI + Spring Boot3.x + JDK 21...点击查看;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot3.x + JDK 17...点击查看项目介绍; 演示链接: http://116.62.199.48:7070/;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/

面试考察点

面试官提出这个问题,主要希望考察以下几个层面:

  1. 对设计模式的基本理解:你是否能清晰、准确地阐述一种经典设计模式的定义和核心思想。
  2. 对模式结构和运行机制的掌握:你能否描述出该模式的关键角色(如 HandlerConcreteHandler)以及它们之间如何协作,构成“链”并传递请求。
  3. 理论与实践结合的能力:这往往是面试官不仅仅想知道理论,更是想知道你是否能在实际开发中识别并应用该模式。列举的应用场景是否典型、合理,能否说明使用该模式带来的好处(如解耦、灵活性)。
  4. 对模式优劣和细节的认知:你是否了解责任链模式的优缺点,以及在实现时需要注意的常见陷阱(例如,保证链的完整性、处理默认行为等)。

核心答案

责任链模式 是一种行为型设计模式。它允许多个对象(处理器)都有机会处理同一个请求,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。核心目的在于将请求的发送者与接收者解耦,让多个接收对象都有机会处理请求,同时也可以动态地修改处理链。

典型的应用场景包括:

  1. 多级请求处理:如员工请假、费用报销审批流程(需经组长、经理、总监等多级审批)。
  2. 过滤器和拦截器:Servlet 中的 FilterChain、Spring MVC 的 HandlerInterceptor
  3. 日志记录器:不同级别的日志(DEBUG、INFO、ERROR)被不同记录器处理。
  4. 异常处理:某些框架中,异常会在一系列处理器中传递,寻找能处理该异常类型的处理器。

深度解析

原理/机制

该模式的核心是建立一个处理器链。每个处理器都包含一个对下一个处理器的引用。当一个请求到来时,处理器先判断自己能否处理。如果能,则处理并结束;如果不能(或处理完后选择继续传递),则将请求转发给链中的下一个处理器。

关键角色

  • 抽象处理器(Handler):定义处理请求的接口(或抽象类),通常包含一个处理请求的方法(如 handleRequest)和一个设置下一个处理器的属性(如 successor)。
  • 具体处理器(ConcreteHandler):实现抽象处理器的接口,负责处理它所能负责的请求。如果可以处理,则处理;否则,将请求转发给后继者。
  • 客户端(Client):负责组装责任链,并将请求提交给链上的第一个处理器。

代码示例

以下是一个简化的请假审批流程示例:

// 1. 抽象处理器
abstract class Approver {
    protected Approver nextApprover; // 持有下一个处理者的引用
    protected String name;

    public Approver(String name) {
        this.name = name;
    }

    // 设置下一个处理者(用于构建链)
    public Approver setNext(Approver nextApprover) {
        this.nextApprover = nextApprover;
        return this.nextApprover; // 支持链式调用,方便构建
    }

    // 处理请求的核心方法
    public abstract void processRequest(LeaveRequest request);
}

// 2. 具体处理器:小组长
class GroupLeader extends Approver {
    public GroupLeader(String name) { super(name); }

    @Override
    public void processRequest(LeaveRequest request) {
        if (request.getDays() <= 2) {
            System.out.println(name + "(组长)审批了" + request.getName() + "的请假,天数为:" + request.getDays());
        } else if (nextApprover != null) {
            System.out.println(name + "(组长)无权审批,转交上级。");
            nextApprover.processRequest(request); // 传递给下一级
        } else {
            System.out.println("链已结束,无人能处理该请求。");
        }
    }
}

// 2. 具体处理器:部门经理
class Manager extends Approver {
    public Manager(String name) { super(name); }

    @Override
    public void processRequest(LeaveRequest request) {
        if (request.getDays() <= 7) {
            System.out.println(name + "(经理)审批了" + request.getName() + "的请假,天数为:" + request.getDays());
        } else if (nextApprover != null) {
            System.out.println(name + "(经理)无权审批,转交上级。");
            nextApprover.processRequest(request);
        }
    }
}

// 请求类
class LeaveRequest {
    private String name;
    private int days;
    // 省略构造方法和getter/setter
}

// 3. 客户端(组装链并提交请求)
public class Client {
    public static void main(String[] args) {
        // 构建责任链: GroupLeader -> Manager (还可以继续追加总监等)
        Approver groupLeader = new GroupLeader("张组长");
        Approver manager = new Manager("李经理");
        groupLeader.setNext(manager); // 设置链关系

        // 提交不同请求
        LeaveRequest request1 = new LeaveRequest("小王", 1);
        LeaveRequest request2 = new LeaveRequest("老李", 5);
        LeaveRequest request3 = new LeaveRequest("大刘", 10);

        groupLeader.processRequest(request1); // 张组长处理
        groupLeader.processRequest(request2); // 张组长转交李经理处理
        groupLeader.processRequest(request3); // 层层转交,本例中链末端,无人处理
    }
}

对比分析与注意事项

  • 与装饰器模式的区别:两者结构相似(都包含对同类对象的引用),但目的不同。责任链模式的各个处理器是 “互斥” 的,一个请求通常只被一个处理器处理(或无人处理),核心是传递与选择装饰器模式的各个装饰者是 “叠加” 的,所有装饰者都会参与处理,核心是功能增强

最佳实践与常见误区

  • 动态构建链:责任链应在运行时可以灵活配置和修改,这是其核心优势之一。可以通过配置文件、数据库或依赖注入框架(如 Spring)来管理链的组成。
  • 确保链的完整性:实现时,务必在处理器末尾进行判空(if (nextHandler != null)),防止空指针异常,并为无法处理的请求提供默认行为(如记录日志、抛出异常或使用一个 “最终处理器”)。
  • 性能考量:如果链过长,且大部分请求需要传递到末端才被处理,可能会影响性能。在设计时,可以将最可能被处理的处理器放在链的前端,或根据请求特征进行智能路由(但这会增加复杂性)。
  • 常见误区:在具体处理器中忘记调用下一个处理器nextHandler.handle(request)),导致责任链断裂,请求无法继续传递。

总结

责任链模式通过构建一条处理器链,将请求的发送与具体处理解耦,提供了灵活扩展处理流程的优秀能力,特别适用于多级审批、过滤拦截等场景,但使用时需注意链的完整性和性能。