责任链设计模式

4 阅读6分钟

什么是责任链设计模式?🤔

责任链模式旨在避免请求发送者与接收者之间的耦合,让多个对象都有机会处理这个请求。它将这些对象连成一条链,并沿着这条链传递该请求,直到链上的某个对象处理它为止。

这个模式的核心组件通常包括:

  • • 抽象处理器 (Handler):  定义一个处理请求的接口,以及一个可选的指向链中下一个处理器的引用。
  • • 具体处理器 (Concrete Handler):  实现处理器接口,负责处理它感兴趣的请求。如果它不能处理,就把请求转发给它的下一个处理器。
  • • 客户端 (Client):  创建并组装这条责任链,然后将请求发送到链的第一个处理器。

在 Spring Boot 的场景下,我们可以利用其强大的特性(如 @Order 注解和 List 注入)来自动地、声明式地构建和管理这条链,而无需手动连接。

为什么要在 Spring Boot 中使用责任链模式?💡

责任链模式能带来诸多好处:

  • • 高度解耦 (Decoupling):  请求的发送方完全不知道是链上的哪个对象最终处理了请求。同时,链上的各个处理器也只关心自己的下一个节点,彼此之间互不知晓。
  • • 职责单一 (Single Responsibility):  每个处理器只专注于自己的一项任务(如日志记录、权限校验、参数验证),使得代码更清晰、更易于测试和维护。
  • • 灵活的组合与扩展 (Flexibility & Extensibility):  你可以非常方便地在链中增加、移除或重新排序处理器,而无需修改任何现有处理器的代码,完美符合开闭原则。
  • • 清晰的流程控制 (Clear Flow Control):  将一个复杂的、线性的处理流程,拆解成一系列独立的、可插拔的步骤,使得整个业务流程一目了然。
  • • 与Spring无缝集成 (Spring Integration):  Spring 的依赖注入和自动排序机制是实现责任链模式的绝佳工具。著名的 Spring Security 过滤器链(FilterChain)就是该模式的经典应用。

问题所在:臃肿的过程式代码

假设你正在构建一个API接口,用于处理用户提交的某个请求。在一个方法里,你可能需要完成一系列的检查:

public void handleRequest(RequestData data) {
    // 步骤1:检查请求参数是否为空
    if (data.getFieldA() == null || data.getFieldB().isEmpty()) {
        throw new IllegalArgumentException("参数不合法");
    }
    
    // 步骤2:记录请求日志
    logger.info("收到请求: {}", data);
    
    // 步骤3:检查用户权限
    User user = getCurrentUser();
    if (!user.hasPermission("SOME_PERMISSION")) {
        throw new SecurityException("无权操作");
    }
    
    // 步骤4:检查接口调用频率
    if (isRateLimited(user.getId())) {
        throw new RateLimitException("请求过于频繁");
    }

    // 步骤5:执行真正的业务逻辑...
    processBusiness(data);
}

这个方法很快就会变得难以维护:

❌ 违反单一职责原则:  一个方法承担了验证、日志、鉴权、限流等多种职责。
❌ 难以复用和重排:  如果另一个接口也需要鉴权和限流,但不需要参数校验,你就得复制粘贴代码,或者把逻辑拆得更乱。
❌ 扩展困难:  如果想在“鉴权”和“限流”之间增加一个“黑名单校验”,就必须修改这个方法的内部代码。

✅ 责任链模式来修复
责任链模式允许你将上述每一个步骤都封装成一个独立的处理器对象,然后将它们串联起来,形成一条处理流水线。

一步步实现 Java 示例:审批流程链

让我们为一个简单的报销审批流程实现责任链模式。

第一步:定义抽象处理器 (Abstract Handler)

// 报销单
class ReimbursementRequest {
    private double amount;
    // getter...
}

// 抽象处理器
public abstract class Approver {
    protected Approver nextApprover;
    protected String name;

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

    public void setNext(Approver next) {
        this.nextApprover = next;
    }
    
    public abstract void processRequest(ReimbursementRequest request);
}

第二步:创建具体处理器 (Concrete Handlers)

// 项目经理
public class ProjectManager extends Approver {
    public ProjectManager(String name) { super(name); }
    
    @Override
    public void processRequest(ReimbursementRequest request) {
        if (request.getAmount() <= 500) {
            System.out.println(this.name + " (项目经理) 审批通过。");
        } else if (nextApprover != null) {
            System.out.println(this.name + " (项目经理) 无权审批,转交上级。");
            nextApprover.processRequest(request);
        }
    }
}

// 部门总监
public class DepartmentHead extends Approver {
    public DepartmentHead(String name) { super(name); }
    
    @Override
    public void processRequest(ReimbursementRequest request) {
        if (request.getAmount() <= 5000) {
            System.out.println(this.name + " (部门总监) 审批通过。");
        } else if (nextApprover != null) {
            System.out.println(this.name + " (部门总监) 无权审批,转交上级。");
            nextApprover.processRequest(request);
        }
    }
}

第三步:客户端组装并使用

public class Main {
    public static void main(String[] args) {
        // 组装责任链
        Approver manager = new ProjectManager("张经理");
        Approver head = new DepartmentHead("李总监");
        manager.setNext(head);
        
        // 发起请求
        manager.processRequest(new ReimbursementRequest(400));  // 项目经理处理
        manager.processRequest(new ReimbursementRequest(3000)); // 部门总监处理
        manager.processRequest(new ReimbursementRequest(8000)); // 部门总监也无法处理
    }
}

✅ 职责清晰 ✅ 链条灵活可变

Spring Boot 应用案例:订单处理流水线

在 Spring Boot 中,我们可以利用依赖注入和 @Order 注解,优雅地构建责任链。

第一步:定义统一的处理器接口和上下文

// 用于在链中传递的上下文对象
public class OrderContext { /* 包含订单信息、用户信息等 */ }

// 处理器接口
public interface OrderHandler {
    void handle(OrderContext context);
}

第二步:实现各种处理器并排序
每个处理器都是一个 Spring Bean,并用 @Order 指定其在链中的顺序。

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(1) // 序号最小,最先执行
public class ValidationHandler implements OrderHandler {
    @Override
    public void handle(OrderContext context) {
        System.out.println("【第一关】: 参数校验通过。");
        // 注意:在Spring的实现中,我们不在此处手动调用下一个
    }
}

@Component
@Order(2)
public class PromotionHandler implements OrderHandler {
    @Override
    public void handle(OrderContext context) {
        System.out.println("【第二关】: 优惠活动校验通过。");
    }
}

@Component
@Order(3)
public class StockHandler implements OrderHandler {
    @Override
    public void handle(OrderContext context) {
        System.out.println("【第三关】: 库存检查通过。");
        // 假设库存不足,可以抛出异常来中断链的执行
        // if (stockNotEnough) { throw new RuntimeException("库存不足!"); }
    }
}

第三步:创建责任链的“管道”执行器
这个服务是关键,它注入所有 OrderHandler 的实例,并按顺序执行它们。

import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class OrderHandlerPipeline {

    private final List<OrderHandler> handlers;

    // Spring会自动将所有OrderHandler类型的Bean,按@Order排序后注入到这个List中
    public OrderHandlerPipeline(List<OrderHandler> handlers) {
        this.handlers = handlers;
    }

    public void process(OrderContext context) {
        // 简单地遍历并执行链上的每一个处理器
        for (OrderHandler handler : handlers) {
            handler.handle(context);
        }
    }
}

第四步:REST 控制器

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/orders")
public class OrderController {
    private final OrderHandlerPipeline pipeline;

    public OrderController(OrderHandlerPipeline pipeline) {
        this.pipeline = pipeline;
    }

    @PostMapping("/create")
    public ResponseEntity<String> createOrder(@RequestBody OrderData data) {
        try {
            OrderContext context = new OrderContext(data);
            pipeline.process(context);
            return ResponseEntity.ok("订单创建流程处理完毕!");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("订单处理失败: " + e.getMessage());
        }
    }
}

责任链 vs. 策略模式

  • • 意图不同:  责任链的意图是让多个对象都有机会处理一个请求,形成一条“流水线”。策略模式的意图是从多个算法中选择一个来执行。
  • • 结构不同:  责任链是多个处理器的“链式”或“管道式”结构。策略模式是一个上下文对象持有一个策略对象的引用。
  • • 行为不同:  责任链中的请求可以被链上的任意一个或多个处理器处理(取决于实现),也可以中途断开。策略模式一旦选定,通常会完整执行那一个策略。

✅ 何时使用责任链模式

  • • 当一个请求需要经过多个步骤,且这些步骤需要解耦、可复用、可重排时。
  • • 当你希望一个请求的接收者和处理者是动态确定的时候。
  • • 当你想实现类似Java Servlet或Spring Security中的过滤器链(Filter Chain)功能时。