设计模式——责任链模式

88 阅读6分钟

🎯 一、模式概述

1.1 什么是责任链模式?

责任链模式(Chain of Responsibility Pattern) 是一种行为型设计模式,它允许多个对象都有机会处理同一个请求,从而避免请求的发送者与接收者之间的耦合关系。这些处理对象被连成一条链,并沿着这条链传递请求,直到有对象处理它为止。


1.2 模式目的

  • 解耦请求发送者与接收者:发送者无需知道哪个对象会处理请求。
  • 动态构建处理流程:可以在运行时决定由哪些对象来处理请求,以及它们的顺序。
  • 增强灵活性与可扩展性:新增处理逻辑时无需修改已有代码,符合开闭原则。

1.3 模式类型

分类类型
设计模式分类行为型模式(Behavioral Pattern)

🧩 二、模式结构

责任链模式由以下主要角色组成:

2.1 角色说明

角色说明
Handler(抽象处理者)定义处理请求的接口,通常包含一个处理方法(如 handle() / process()),以及一个指向下一个处理者的引用(next / successor)。
ConcreteHandler(具体处理者)实现抽象处理者接口,处理它所负责的请求;如果不能处理,则将请求传递给链中的下一个处理者。
Client(客户端)创建处理者链,并向链上的第一个处理者提交请求。

2.2 类图(UML 结构)

+----------------+       +---------------------+       +---------------------+
|    Client      |------>|     Handler         |<------|   ConcreteHandler   |
+----------------+       +---------------------+       +---------------------+
|                |       | +setNextHandler()   |       | +processRequest()   |
| +trigger()     |       | +processRequest()   |       | (具体处理逻辑)     |
+----------------+       +----------+----------+       +----------+----------+
                                           ^
                                           |
                           +----------+----------+
                           |   ConcreteHandler2  |
                           +---------------------+
                           | +processRequest()   |
                           +---------------------+

责任链模式类图

🖼 Client 调用 Handler 链,Handler 是抽象类,下面有若干具体实现(如 ConcreteHandler1/2/3),每个 Handler 持有一个对下一个 Handler 的引用(nextHandler)。


💼 三、适用场景

责任链模式适用于以下场景:

场景说明
多对象可选处理请求请求可由多个对象中的一个处理,但具体由谁处理在运行时决定
审批流程如请假、报销等逐级审批,不同级别处理不同范围
过滤器 / 拦截器如 Web 中的 Filter、Spring Security 过滤器链
日志分级处理不同级别日志由不同 Handler 处理
异常处理机制按异常类型逐级上报或处理
中间件 / 流程管道如 Netty 的 ChannelPipeline、任务处理流水线

🛠 四、示例:请假审批流程

4.1 需求背景

员工请假审批规则如下:

  • 小于等于 3 天,组长审批
  • 4 ~ 7 天,经理审批
  • 8 ~ 15 天,总监审批
  • 超过 15 天,拒绝
  • 后续可扩展如 HR 备案等环节

4.2 传统实现方式的问题(if-else)

public class LeaveService {
    public boolean approve(int days) {
        if(days <= 3) {
            return GroupLeader.approve();
        } else if(days <= 7) {
            return Manager.approve();
        } else if(days <= 15) {
            return Director.approve();
        } else {
            System.out.println("请假天数过长,拒绝审批");
            return false;
        }
    }
}

❌ 缺点:

  • 所有逻辑集中在一个方法中,难以维护;
  • 增加新角色(如 HR)必须修改原代码;
  • 违反开闭原则;
  • 审批流程不灵活,难以动态调整。

4.3 责任链模式改造

4.3.1 抽象处理者(Handler)

// 抽象处理者
public abstract class Approver {
    protected Approver nextApprover;

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

    // 处理请求的抽象方法
    public abstract void processRequest(int days);
}

4.3.2 具体处理者(ConcreteHandler)

// 组长
public class TeamLeader extends Approver {
    @Override
    public void processRequest(int days) {
        if (days <= 3) {
            System.out.println("组长批准了 " + days + " 天的请假");
        } else if (nextApprover != null) {
            nextApprover.processRequest(days);
        } else {
            System.out.println("异常:未设置后续审批人!");
        }
    }
}

// 经理
public class Manager extends Approver {
    @Override
    public void processRequest(int days) {
        if (days > 3 && days <= 7) {
            System.out.println("经理批准了 " + days + " 天的请假");
        } else if (nextApprover != null) {
            nextApprover.processRequest(days);
        } else {
            System.out.println("异常:未设置后续审批人!");
        }
    }
}

// 总监
public class Director extends Approver {
    @Override
    public void processRequest(int days) {
        if (days > 7 && days <= 15) {
            System.out.println("总监批准了 " + days + " 天的请假");
        } else if (days > 15) {
            System.out.println("驳回大于15天的假期请求!");
        } else if (nextApprover != null) {
            nextApprover.processRequest(days);
        }
    }
}

链终止控制:组长和经理处理器中保留nextApprover != null判断,防止空指针;总监处理器作为终审节点,不再传递请求。 业务属性扩展:days可以封装为请求对象,扩展更多业务属性。

4.3.3 责任链组装(ChainBuilder)

public class ChainBuilder {
    public static Approver buildChain() {
        Approver teamLeader = new TeamLeader();
        Approver manager = new Manager();
        Approver director = new Director();

        return teamLeader
                .setNextApprover(manager)
                .setNextApprover(director);
    }
}

可以增加空值校验,比如

Approver teamLeader = new TeamLeader();
if(teamLeader == null) 
    throw new IllegalArgumentException();
Approver manager = new Manager();
if(manager == null) 
    throw new IllegalArgumentException();
Approver director = new Director();
if(director == null) 
    throw new IllegalArgumentException();

4.3.4 客户端调用

public class Client {
    public static void main(String[] args) {
        Approver chain = ChainBuilder.buildChain();

        System.out.println("=== 请假2天 ===");
        chain.processRequest(2);

        System.out.println("\n=== 请假5天 ===");
        chain.processRequest(5);

        System.out.println("\n=== 请假10天 ===");
        chain.processRequest(10);

        System.out.println("\n=== 请假20天 ===");
        chain.processRequest(20);
    }
}

✅ 输出结果:

=== 请假2天 ===
组长批准了 2 天的请假

=== 请假5天 ===
经理批准了 5 天的请假

=== 请假10天 ===
总监批准了 10 天的请假

=== 请假20天 ===
驳回大于15天的假期请求!

4.4 扩展示例:增加 HR 备案

// HR备案处理器
public class HrRecorder extends Approver {
    @Override
    public void processRequest(int days) {
        System.out.println("HR备案 [" + days + "] 天请假记录");
        // 备案后不继续传递
    }
}

更新 ChainBuilder:

public class ChainBuilder {
    public static Approver buildChain() {
        Approver teamLeader = new TeamLeader();
        Approver manager = new Manager();
        Approver director = new Director();
        Approver hr = new HrRecorder();

        return teamLeader
                .setNextApprover(manager)
                .setNextApprover(director)
                .setNextApprover(hr);
    }
}

4.5 可能遇到的问题

  1. 循环链 表象:A->B->A形成死循环。 解决:在setNextApprover方法中增加环路检测。

    public Approver setNextApprover(Approver nextApprover) {
        if (isInChain(nextApprover)) throw new IllegalArgumentException("环路检测未通过!");
        this.nextApprover = nextApprover;
        return nextApprover;
    }
    
  2. 请求丢失 表象:所有处理器都不处理请求 解决:添加默认处理器作为链尾

    public class DefaultHandler extends Approver {
        @Override
        public void process(LeaveRequest request) {
            System.out.println("请求未被任何处理器处理!");
        }
    }
    
  3. 顺序敏感 表象:先添加经理再添加组长 解决:使用建造者模式封装链条构建 public class ChainBuilder { private List handlers = new ArrayList<>();

       public ChainBuilder addHandler(Approver handler) {
           handlers.add(handler);
           return this;
       }
       
       public Approver build() {
           for(int i = 0; i< handlers.size() - 1;i++){
               handlers.get(i).setNextApprover(handlers.get(i+1));
           }
           return handlers.get(0);
       }
    }
    

✅ 五、责任链模式的优点

优点说明
低耦合发送者无需知道具体由谁处理请求
高扩展性可动态添加/移除/重排序处理者
单一职责每个处理者只关注自己的逻辑
开闭原则新增处理逻辑无需修改已有代码

⚠️ 六、责任链模式的缺点

缺点说明
请求可能未被处理如果链配置不正确,请求可能到达链尾都未被处理
调试复杂请求传递过程不直观,排查问题需跟踪整个链条
性能影响请求可能经过多个处理者,链过长时影响性能

🧩 七、Java 中的常见应用场景

场景说明
Servlet Filter请求/响应经过一组过滤器链
Spring Security多个安全过滤器组成责任链进行拦截处理
日志系统不同级别日志由不同处理器处理
异常处理机制按异常类型逐层上报或处理
中间件/拦截器如 Netty 的 ChannelPipeline

📋 八、总结

项目说明
模式名称责任链模式(Chain of Responsibility Pattern)
类型行为型设计模式
目的解耦请求发送者与接收者,让多个对象都有机会处理请求
核心机制请求沿处理者链传递,直到被处理或到达链尾
优点灵活、解耦、可扩展、符合开闭原则
缺点可能请求未被处理、调试困难、性能问题
应用审批流程、过滤器、日志系统、异常处理等

📎 九、附录:UML 类图

责任链模式

🖼 Client 调用抽象 Approver,其下有 TeamLeader、Manager、Director、HrRecorder 等具体处理者,每个处理者持有一个指向下一个处理者的引用。