公众号:【CTO成长日记】

Java 设计模式之责任链模式:优雅处理请求的艺术

在软件开发中,我们经常遇到需要多个对象处理同一请求的场景。比如请假审批流程、日志级别处理、异常处理等,这些场景都有一个共同特点:请求需要经过一系列处理步骤,每个步骤可能处理请求,也可能将其传递给下一个步骤。责任链模式(Chain of Responsibility Pattern)正是为这类问题提供了优雅的解决方案。

什么是责任链模式?

责任链模式是一种行为型设计模式,它创建了一个对象链,每个对象都有机会处理请求。请求沿着这条链传递,直到有一个对象处理它为止,或者直到链的末端。

这种模式的核心思想是:解耦请求的发送者和接收者,让多个接收者都有机会处理请求,而发送者不需要知道具体哪个接收者处理了请求。

责任链模式的核心角色

  1. 抽象处理者(Handler):定义处理请求的接口,包含一个指向 nextHandler 的引用
  2. 具体处理者(ConcreteHandler):实现抽象处理者接口,判断能否处理请求,如果能则处理,否则将请求传递给下一个处理者
  3. 请求(Request):需要被处理的对象

责任链模式的实现方式

让我们通过一个实际案例来理解责任链模式。假设我们要实现一个请假审批系统,规则如下:

  • 请假 1-3 天:部门主管审批
  • 请假 4-7 天:部门经理审批
  • 请假 8-30 天:总经理审批
  • 请假超过 30 天:不予批准

步骤 1:创建请求类

首先定义一个请假请求类,包含请假人、请假天数等信息:

// 请假请求类
public class LeaveRequest {
    private String employeeName; // 员工姓名
    private int leaveDays;       // 请假天数
    
    public LeaveRequest(String employeeName, int leaveDays) {
        this.employeeName = employeeName;
        this.leaveDays = leaveDays;
    }
    
    // getter方法
    public String getEmployeeName() {
        return employeeName;
    }
    
    public int getLeaveDays() {
        return leaveDays;
    }
}

步骤 2:创建抽象处理者

定义一个审批者抽象类,作为所有具体审批者的父类:

// 抽象审批者
public abstract class Approver {
    // 下一个审批者
    protected Approver nextApprover;
    
    // 设置下一个审批者
    public void setNextApprover(Approver nextApprover) {
        this.nextApprover = nextApprover;
    }
    
    // 处理请假请求的抽象方法
    public abstract void processRequest(LeaveRequest request);
}

步骤 3:创建具体处理者

分别实现部门主管、部门经理和总经理这三个具体处理者:

// 部门主管
public class Director extends Approver {
    @Override
    public void processRequest(LeaveRequest request) {
        // 处理1-3天的请假
        if (request.getLeaveDays() >= 1 && request.getLeaveDays() <= 3) {
            System.out.println(
                "部门主管批准 " + request.getEmployeeName() + 
                " 请假 " + request.getLeaveDays() + " 天"
            );
        } 
        // 超过3天则交给下一个审批者
        else if (nextApprover != null) {
            nextApprover.processRequest(request);
        }
    }
}

// 部门经理
public class Manager extends Approver {
    @Override
    public void processRequest(LeaveRequest request) {
        // 处理4-7天的请假
        if (request.getLeaveDays() >= 4 && request.getLeaveDays() <= 7) {
            System.out.println(
                "部门经理批准 " + request.getEmployeeName() + 
                " 请假 " + request.getLeaveDays() + " 天"
            );
        } 
        // 超过7天则交给下一个审批者
        else if (nextApprover != null) {
            nextApprover.processRequest(request);
        }
    }
}

// 总经理
public class GeneralManager extends Approver {
    @Override
    public void processRequest(LeaveRequest request) {
        // 处理8-30天的请假
        if (request.getLeaveDays() >= 8 && request.getLeaveDays() <= 30) {
            System.out.println(
                "总经理批准 " + request.getEmployeeName() + 
                " 请假 " + request.getLeaveDays() + " 天"
            );
        } 
        // 超过30天则不予批准
        else {
            System.out.println(
                request.getEmployeeName() + " 请假 " + request.getLeaveDays() + 
                " 天,超过最大允许天数,不予批准"
            );
        }
    }
}

步骤 4:客户端使用

创建责任链并测试不同的请假请求:

public class Client {
    public static void main(String[] args) {
        // 创建各个审批者
        Approver director = new Director();
        Approver manager = new Manager();
        Approver generalManager = new GeneralManager();
        
        // 构建责任链:部门主管 -> 部门经理 -> 总经理
        director.setNextApprover(manager);
        manager.setNextApprover(generalManager);
        
        // 创建不同的请假请求
        LeaveRequest request1 = new LeaveRequest("张三", 2);
        LeaveRequest request2 = new LeaveRequest("李四", 5);
        LeaveRequest request3 = new LeaveRequest("王五", 15);
        LeaveRequest request4 = new LeaveRequest("赵六", 35);
        
        // 处理请求
        director.processRequest(request1);
        director.processRequest(request2);
        director.processRequest(request3);
        director.processRequest(request4);
    }
}

运行结果:

部门主管批准 张三 请假 2 天
部门经理批准 李四 请假 5 天
总经理批准 王五 请假 15 天
赵六 请假 35 天,超过最大允许天数,不予批准

责任链模式的变种

除了上述线性责任链,还有其他几种常见的责任链形式:

  1. 环形责任链:最后一个处理者指向第一个处理者
  2. 树形责任链:一个处理者可以有多个后继者
  3. 带条件的责任链:根据条件决定下一个处理者

责任链模式的优缺点

优点

  1. 降低耦合度:请求的发送者和接收者解耦
  2. 灵活性高:可以灵活地改变链的结构和顺序
  3. 符合开闭原则:新增处理者不需要修改原有代码
  4. 简化对象:每个对象只需关注自己的职责范围

缺点

  1. 请求可能不被处理:如果链中没有对象处理请求,请求可能会被丢弃
  2. 调试复杂:请求的处理路径可能较长,调试时需要跟踪整个链
  3. 性能影响:对于长链,可能会有一定的性能开销

责任链模式的应用场景

  1. 审批流程:如请假、报销、采购等多级审批
  2. 日志系统:不同级别日志由不同处理器处理
  3. 过滤器 / 拦截器:如 Web 中的过滤器链、Spring 的拦截器链
  4. 异常处理:多层异常捕获处理
  5. 事件冒泡:如 GUI 中的事件传递机制

Java 中的责任链模式实例

在 Java 标准库和主流框架中,责任链模式有很多应用:

  1. Servlet Filter:所有 Filter 组成一个链,依次处理请求
  2. Spring Interceptor:拦截器链对请求进行预处理和后处理
  3. Netty ChannelPipeline:处理网络事件的责任链
  4. Java 异常处理:try-catch 块的嵌套本质上也是一种责任链

总结

责任链模式通过构建处理者对象链,让请求在链中传递直至被处理,有效解耦了请求发送者和接收者。它特别适合处理那些需要多级处理或动态调整处理流程的场景。

在实际开发中,使用责任链模式时需要注意:

  • 避免构建过长的责任链,以免影响性能
  • 确保每个请求都能被适当处理,避免无人处理的情况
  • 合理设计处理者的职责边界,保持单一职责原则

掌握责任链模式,能让我们的代码更加灵活、可扩展,更好地应对复杂业务场景的变化。

原文:CTO成长日记

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐