【设计模式】【行为型模式】职责链模式(Chain of Responsibility)

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD

🔥 2025本人正在沉淀中... 博客更新速度++

📫 欢迎+V: flzjcsg2,我们共同讨论Java深渊的奥秘

🎵 当你的天空突然下了大雨,那是我在为你炸乌云

文章目录

一、入门

什么是职责链模式?

职责链模式是一种行为设计模式,它允许你将请求沿着一条链传递,直到有对象处理它为止。每个对象都有机会处理请求,或者将其传递给链中的下一个对象。

为什么需要职责链模式?

使用职责链模式的好处:

  • 解耦:发送者和处理者不直接依赖,各自独立。
  • 灵活:动态调整处理顺序,易于扩展。
  • 清晰:每个对象只处理自己能做的事,职责单一。
  • 避免硬编码:处理逻辑由链决定,代码更灵活。

怎样实现职责链模式?

责任链模式主要包含以下角色:

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接(下一个对象的引用)。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

【案例】请假审批

现需要开发一个请假流程控制系统。请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部门经理同意;请求3天到7天还需要总经理同意才行。

请假条

java 复制代码
public class LeaveRequest {
    private String name;//姓名
    private int num;//请假天数
    private String content;//请假内容

    public LeaveRequest(String name, int num, String content) {
        this.name = name;
        this.num = num;
        this.content = content;
    }

    public String getName() {
        return name;
    }

    public int getNum() {
        return num;
    }

    public String getContent() {
        return content;
    }
}

处理者抽象类

java 复制代码
public abstract class Handler {
    protected final static int NUM_ONE = 1;
    protected final static int NUM_THREE = 3;
    protected final static int NUM_SEVEN = 7;

    //该领导处理的请假天数区间
    private int numStart;
    private int numEnd;

    //领导上面还有领导
    private Handler nextHandler;

    //设置请假天数范围 上不封顶
    public Handler(int numStart) {
        this.numStart = numStart;
    }

    //设置请假天数范围
    public Handler(int numStart, int numEnd) {
        this.numStart = numStart;
        this.numEnd = numEnd;
    }

    //设置上级领导
    public void setNextHandler(Handler nextHandler){
        this.nextHandler = nextHandler;
    }

    //提交请假条
    public final void submit(LeaveRequest leave){
        if(0 == this.numStart){
            return;
        }

        //如果请假天数达到该领导者的处理要求
        if(leave.getNum() >= this.numStart){
            this.handleLeave(leave);

            //如果还有上级 并且请假天数超过了当前领导的处理范围
            if(null != this.nextHandler && leave.getNum() > numEnd){
                this.nextHandler.submit(leave);//继续提交
            } else {
                System.out.println("流程结束");
            }
        }
    }

    //各级领导处理请假条方法
    protected abstract void handleLeave(LeaveRequest leave);
}

小组长

java 复制代码
public class GroupLeader extends Handler {
    public GroupLeader() {
    //小组长处理1-3天的请假
        super(Handler.NUM_ONE, Handler.NUM_THREE);
    }

    @Override
    protected void handleLeave(LeaveRequest leave) {
        System.out.println(leave.getName() + "请假" + leave.getNum() +
                "天," + leave.getContent() + "。");
        System.out.println("小组长审批:同意。");
    }
}

部门经理

java 复制代码
public class Manager extends Handler {
    public Manager() {
        //部门经理处理3-7天的请假
        super(Handler.NUM_THREE, Handler.NUM_SEVEN);
    }

    @Override
    protected void handleLeave(LeaveRequest leave) {
        System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
        System.out.println("部门经理审批:同意。");
    }
}

总经理

java 复制代码
public class GeneralManager extends Handler {
    public GeneralManager() {
        //部门经理处理7天以上的请假
        super(Handler.NUM_SEVEN);
    }

    @Override
    protected void handleLeave(LeaveRequest leave) {
        System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
        System.out.println("总经理审批:同意。");
    }
}

测试

java 复制代码
public class Client {
    public static void main(String[] args) {
        //请假条来一张
        LeaveRequest leave = new LeaveRequest("小花",5,"身体不适");

        //各位领导
        GroupLeader groupLeader = new GroupLeader();
        Manager manager = new Manager();
        GeneralManager generalManager = new GeneralManager();

        groupLeader.setNextHandler(manager);//小组长的领导是部门经理
        manager.setNextHandler(generalManager);//部门经理的领导是总经理
        //之所以在这里设置上级领导,是因为可以根据实际需求来更改设置,如果实战中上级领导人都是固定的,则可以移到领导实现类中。

        //提交申请
        groupLeader.submit(leave);
    }
}

二、职责链模式在源码中的运用

2.1、Spring MVC 拦截器

Spring MVC 中的拦截器也使用了责任链模式。每个拦截器可以在请求处理前后执行特定逻辑,并决定是否继续传递请求。

2.1.1、如何使用SpringMVC拦截器?

实现HandlerInterceptor接口,并重写它的三个方法:

  • preHandle:在请求到达控制器之前执行。
  • postHandle:在控制器处理请求之后、视图渲染之前执行。
  • afterCompletion:在视图渲染之后执行(通常用于资源清理)。
java 复制代码
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor: preHandle - Before handling request");
        // 可以在这里进行权限检查、日志记录等操作
        return true; // 返回 true 表示继续传递请求,false 表示中断请求
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor: postHandle - After handling request, before view rendering");
        // 可以在这里修改 ModelAndView
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor: afterCompletion - After view rendering");
        // 可以在这里进行资源清理
    }
}

注册拦截器,将自定义拦截器注册到Spring MVC的配置中。可以通过实现WebMvcConfigurer接口来完成。

java 复制代码
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册自定义拦截器,并指定拦截的路径
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/**") // 拦截所有路径
                .excludePathPatterns("/public/**"); // 排除特定路径
    }
}

方便我们测试创建的contorlller。

java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MyController {

    @GetMapping("/hello")
    @ResponseBody
    public String hello() {
        System.out.println("Controller: Handling /hello request");
        return "Hello, World!";
    }
}

运行之后的结果。

shell 复制代码
MyInterceptor: preHandle - Before handling request
Controller: Handling /hello request
MyInterceptor: postHandle - After handling request, before view rendering
MyInterceptor: afterCompletion - After view rendering

2.1.2、SpringMVC拦截器是如何实现的?

在 Spring MVC 中,HandlerExecutionChain 是责任链模式的实现核心。它包含了一个处理器(Handler)和一组拦截器(Interceptors),并负责按顺序调用这些拦截器的方法。

java 复制代码
public class HandlerExecutionChain {

    private final Object handler; // 处理器(通常是Controller方法)
    private final List<HandlerInterceptor> interceptorList = new ArrayList<>(); // 拦截器列表

    // 添加拦截器
    public void addInterceptor(HandlerInterceptor interceptor) {
        this.interceptorList.add(interceptor);
    }

    // 执行拦截器的 preHandle 方法
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        for (int i = 0; i < this.interceptorList.size(); i++) {
            HandlerInterceptor interceptor = this.interceptorList.get(i);
            if (!interceptor.preHandle(request, response, this.handler)) {
                // 如果某个拦截器返回 false,则中断请求
                triggerAfterCompletion(request, response, null);
                return false;
            }
        }
        return true;
    }

    // 执行拦截器的 postHandle 方法
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
        for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = this.interceptorList.get(i);
            interceptor.postHandle(request, response, this.handler, mv);
        }
    }

    // 执行拦截器的 afterCompletion 方法
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception {
        for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = this.interceptorList.get(i);
            interceptor.afterCompletion(request, response, this.handler, ex);
        }
    }
}

DispatcherServlet 是 Spring MVC 的核心组件,它负责将请求分发给对应的处理器,并在处理过程中调用拦截器链。

java 复制代码
public class DispatcherServlet extends FrameworkServlet {

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 获取处理器和拦截器链
        HandlerExecutionChain mappedHandler = getHandler(request);

        // 调用拦截器的 preHandle 方法
        if (!mappedHandler.applyPreHandle(request, response)) {
            return; // 如果某个拦截器中断请求,则直接返回
        }

        // 调用处理器(Controller方法)
        ModelAndView mv = handle(processedRequest, response, mappedHandler.getHandler());

        // 调用拦截器的 postHandle 方法
        mappedHandler.applyPostHandle(processedRequest, response, mv);

        // 渲染视图
        render(mv, request, response);

        // 调用拦截器的 afterCompletion 方法
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

拦截器是通过 WebMvcConfigurer 接口注册的,Spring 会将这些拦截器添加到HandlerExecutionChain中。

java 复制代码
public class InterceptorRegistration {

    private final HandlerInterceptor interceptor;
    private final List<String> pathPatterns = new ArrayList<>();

    public InterceptorRegistration addPathPatterns(String... patterns) {
        this.pathPatterns.addAll(Arrays.asList(patterns));
        return this;
    }

    public MappedInterceptor getMappedInterceptor() {
        return new MappedInterceptor(pathPatterns.toArray(new String[0]), null, interceptor);
    }
}

2.2、Java Web的Filter Chain

在javaWeb应用开发中,FilterChain是职责链(过滤器)模式的典型应用,以下是Filter的模拟实现分析:模拟web请求Request以及web响应Response。

java 复制代码
public interface Request{
 
}

public interface Response{
 
}

模拟web过滤器Filter

java 复制代码
 public interface Filter {
 	public void doFilter(Request req,Response res,FilterChain c);
 }
模拟实现具体过滤器 
public class FirstFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {

        System.out.println("过滤器1 前置处理");

        // 先执行所有request再倒序执行所有response
        chain.doFilter(request, response);

        System.out.println("过滤器1 后置处理");
    }
}

public class SecondFilter  implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {

        System.out.println("过滤器2 前置处理");

        // 先执行所有request再倒序执行所有response
        chain.doFilter(request, response);

        System.out.println("过滤器2 后置处理");
    }
}

模拟实现过滤器链FilterChain

java 复制代码
public class FilterChain {

    private List<Filter> filters = new ArrayList<Filter>();

    private int index = 0;

    // 链式调用
    public FilterChain addFilter(Filter filter) {
        this.filters.add(filter);
        return this;
    }

    public void doFilter(Request request, Response response) {
        if (index == filters.size()) {
            return;
        }
        Filter filter = filters.get(index);
        index++;
        filter.doFilter(request, response, this);
    }
}

测试

java 复制代码
public class Client {
    public static void main(String[] args) {
        Request  req = null;
        Response res = null ;

        FilterChain filterChain = new FilterChain();
        filterChain.addFilter(new FirstFilter()).addFilter(new SecondFilter());
        filterChain.doFilter(req,res);
    }
}

三、总结

职责链模式:把多个处理对象连成一条链,请求按顺序传递,谁适合处理就处理,处理不了就传给下一个。

优点

  • 解耦:发请求的不用关心谁来处理,处理者之间也不互相依赖。
  • 灵活:能动态调整链条顺序,或增删处理步骤。
  • 单一职责:每个处理者只做自己该做的事。

缺点

  • 可能没人处理:链条没配好时,请求可能"走完全场"也没结果。
  • 性能问题:链条太长时,传递过程拖慢速度。
  • 调试麻烦:请求的传递路径不直观,排查问题费劲。

适用场景

  • 需要动态切换算法或行为:例如,支付方式、排序算法、资源加载策略等。
  • 有多个相似的类,只有行为不同:例如,不同类型的折扣计算、不同的日志记录方式等。
  • 避免使用复杂的条件语句:当代码中有大量if-else或switch-case语句时,可以用策略模式替代。
  • 需要隔离算法的实现细节:当不希望暴露算法的实现细节,或者希望算法可以独立变化时。
  • 需要对算法进行扩展:当系统需要支持新的算法,且不希望修改现有代码时。

参考

黑马程序员Java设计模式详解, 23种Java设计模式(图解+框架源码分析+实战)_哔哩哔哩_bilibili

相关推荐
晨米酱8 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机13 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机14 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机14 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机14 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤14 小时前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴2 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
李广坤2 天前
工厂模式
设计模式