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

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
📫 欢迎+V: flzjcsg2,我们共同讨论Java深渊的奥秘
🎵 当你的天空突然下了大雨,那是我在为你炸乌云

文章目录

  • 一、入门
    • 什么是职责链模式?
    • 为什么需要职责链模式?
    • 怎样实现职责链模式?
  • 二、职责链模式在源码中的运用
    • 2.1、Spring MVC 拦截器
      • 2.1.1、如何使用SpringMVC拦截器?
      • 2.1.2、SpringMVC拦截器是如何实现的?
    • 2.2、Java Web的Filter Chain
  • 三、总结
  • 参考

一、入门

什么是职责链模式?

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

为什么需要职责链模式?

使用职责链模式的好处:

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

怎样实现职责链模式?

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

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

【案例】请假审批
现需要开发一个请假流程控制系统。请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部门经理同意;请求3天到7天还需要总经理同意才行。
在这里插入图片描述

请假条

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;}
}

处理者抽象类

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);
}

小组长

public class GroupLeader extends Handler {public GroupLeader() {//小组长处理1-3天的请假super(Handler.NUM_ONE, Handler.NUM_THREE);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "请假" + leave.getNum() +"天," + leave.getContent() + "。");System.out.println("小组长审批:同意。");}
}

部门经理

public class Manager extends Handler {public Manager() {//部门经理处理3-7天的请假super(Handler.NUM_THREE, Handler.NUM_SEVEN);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");System.out.println("部门经理审批:同意。");}
}

总经理

public class GeneralManager extends Handler {public GeneralManager() {//部门经理处理7天以上的请假super(Handler.NUM_SEVEN);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");System.out.println("总经理审批:同意。");}
}

测试


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:在视图渲染之后执行(通常用于资源清理)。
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("MyInterceptor: preHandle - Before handling request");// 可以在这里进行权限检查、日志记录等操作return true; // 返回 true 表示继续传递请求,false 表示中断请求}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("MyInterceptor: postHandle - After handling request, before view rendering");// 可以在这里修改 ModelAndView}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("MyInterceptor: afterCompletion - After view rendering");// 可以在这里进行资源清理}
}

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

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 {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册自定义拦截器,并指定拦截的路径registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**") // 拦截所有路径.excludePathPatterns("/public/**"); // 排除特定路径}
}

方便我们测试创建的contorlller。

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")@ResponseBodypublic String hello() {System.out.println("Controller: Handling /hello request");return "Hello, World!";}
}

运行之后的结果。

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),并负责按顺序调用这些拦截器的方法。

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 的核心组件,它负责将请求分发给对应的处理器,并在处理过程中调用拦截器链。

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中。

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。

public interface Request{}public interface Response{}

模拟web过滤器Filter

 public interface Filter {public void doFilter(Request req,Response res,FilterChain c);}
模拟实现具体过滤器 
public class FirstFilter implements Filter {@Overridepublic void doFilter(Request request, Response response, FilterChain chain) {System.out.println("过滤器1 前置处理");// 先执行所有request再倒序执行所有responsechain.doFilter(request, response);System.out.println("过滤器1 后置处理");}
}public class SecondFilter  implements Filter {@Overridepublic void doFilter(Request request, Response response, FilterChain chain) {System.out.println("过滤器2 前置处理");// 先执行所有request再倒序执行所有responsechain.doFilter(request, response);System.out.println("过滤器2 后置处理");}
}

模拟实现过滤器链FilterChain

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);}
}

测试

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/69578.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Visual Studio踩过的坑

统计Unity项目代码行数 编辑-查找和替换-在文件中查找 查找内容输入 b*[^:b#/].*$ 勾选“使用正则表达式” 文件类型留空 也有网友做了指定&#xff0c;供参考 !*\bin\*;!*\obj\*;!*\.*\*!*.meta;!*.prefab;!*.unity 打开Unity的项目 注意&#xff1a;只是看&#xff0…

《深度学习》——pytorch框架及项目

文章目录 pytorch特点基本概念 项目项目实现导入所需库下载训练数据和测试数据对训练和测试样本进行分批次展示手写图片判断pytorch是否支持GPU定义神经网络模型定义训练函数定义测试函数创建交叉熵损失函数和优化器通过多轮训练降低损失值得到最终结果注意 pytorch PyTorch 是…

深入探索人工智能的未来:DeepSeek R1与蓝耘智算平台的完美结合

在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;正以前所未有的速度改变着我们的生活和工作方式。从智能语音助手到自动驾驶汽车&#xff0c;从精准医疗到金融风险预测&#xff0c;AI的应用无处不在。深度学习作为AI的核…

基于uniapp vue3 的滑动抢单组件

通过在onMounted获取movable-area与movable-view实例&#xff0c;计算出可滑动的距离 效果图&#xff1a; 代码&#xff1a; <template><view class"slider-container"><movable-area class"movable-area" id"movableArea">…

亚博microros小车-原生ubuntu支持系列:26手势控制小车基础运动

背景知识 手指检测&#xff1a;亚博microros小车-原生ubuntu支持系列&#xff1a;4-手部检测-CSDN博客 程序功能说明 功能开启后&#xff0c;摄像头捕获图像&#xff0c;识别手势来控制小车移动。 手势 “5”小车前进拳头小车后退手势 “1”小车向左手势 “2”小车向右 运…

在人工智能领域 ⊕、⊗和 ⊙ 符号是什么含义?

我们经常在论文中看到 ⊕、⊗和 ⊙ 符号&#xff0c;那么有下面两个问题&#xff1a; 这三个符号有什么作用呢&#xff1f; 如何在论文中正确使用这三个数学符号 1. 两种符号的解释 1.1 逐元素相加&#xff1a;⊕ ⊕ 在论文中表示逐元素相加&#xff0c;如果用两个矩阵表示&a…

NineData云原生智能数据管理平台新功能发布|2025年1月版

本月发布 14 项更新&#xff0c;其中重点发布 6 项、功能优化 7 项、安全性更新 1 项。 重点发布 数据库 Devops - 数据导出功能增强 支持 AWS ElastiCache 数据源&#xff1a;现已支持通过 SQL 查询语句或直接通过库表导出 AWS ElastiCache 数据&#xff0c;方便用户快速提取…

蓝桥与力扣刷题(226 翻转二叉树)

题目&#xff1a;给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1]示例 2&#xff1a; 输入&#xff1a;root [2,1,3] 输出&#xff1a;[2,…

C# OpenCV机器视觉:OSTU算法实现背景差分的自适应分割

在一个热闹的科技公司里&#xff0c;阿强是一个负责图像分析的员工。他的日常工作就是从各种复杂的图像中提取出有用的信息&#xff0c;可这可不是一件轻松的事情哦 最近&#xff0c;阿强接到了一个艰巨的任务&#xff1a;要从一堆嘈杂的监控图像中分离出运动的物体&#xff0c…

amis组件crud使用踩坑

crud注意 过滤条件参数同步地址栏 默认 CRUD 会将过滤条件参数同步至浏览器地址栏中&#xff0c;比如搜索条件、当前页数&#xff0c;这也做的目的是刷新页面的时候还能进入之前的分页。 但也会导致地址栏中的参数数据合并到顶层的数据链中&#xff0c;例如&#xff1a;自动…

解决 ollama._types.ResponseError 问题

原因 在对问题进行分析后&#xff0c;我认为原因是之前为了在服务器上下载模型&#xff0c;我设置了启动时自动配置的网络代理。然而&#xff0c;ollama在运行时采用了该代理配置&#xff0c;而不是默认的API URL&#xff08;“http://localhost:11434”&#xff09;。因此&am…

用户认证实验

一&#xff0c;拓扑图: 第一步&#xff1a;先开启防火墙 第二步&#xff1a;sw2配置&#xff1a; [sw2] vlan batch 10 20 interface GigabitEthernet0/0/2 port link-type access port default vlan 10 interface GigabitEthernet0/0/3 port link-type access port defau…

活动预告 | 为 AI 新纪元做好准备:助力安全的业务转型

课程介绍 随着现代办公模式的不断演变和 AI 技术的迅速发展&#xff0c;企业在享受效率提升的同时&#xff0c;也面临着信息安全与数据保护的严峻挑战。在利用 AI 技术释放业务潜力的同时&#xff0c;如何确保数据质量与安全已成为企业发展的关键议题。 在本次线上课程中&…

【再谈设计模式】中介者模式 - 协调对象间交互的枢纽

一、引言 在软件工程&#xff0c;软件开发过程中&#xff0c;复杂的软件系统&#xff0c;对象之间的交互往往错综复杂。当众多对象相互依赖、频繁通信时&#xff0c;系统的耦合度会急剧上升&#xff0c;导致代码难以维护、扩展和理解。就像在一个大型社交聚会中&#xff0c;如果…

网络工程师 (29)CSMA/CD协议

前言 CSMA/CD协议&#xff0c;即载波监听多路访问/碰撞检测&#xff08;Carrier Sense Multiple Access with Collision Detection&#xff09;协议&#xff0c;是一种在计算机网络中&#xff0c;特别是在以太网环境下&#xff0c;用于管理多个设备共享同一物理传输介质的重要…

软件项目验收测试有哪些类型?

在信息技术行业&#xff0c;软件项目的成功不仅依赖于开发能力&#xff0c;更在于准确的验收测试。验收测试是软件开发生命周期中的重要一环。其主要目的是验证软件系统是否符合用户需求和预期。在这一阶段&#xff0c;最终用户能够直观地判断软件是否满足其业务需求。 软件项…

Python截图轻量化工具

一、兼容局限性 这是用Python做的截图工具&#xff0c;不过由于使用了ctypes调用了Windows的API, 同时访问了Windows中"C:/Windows/Cursors/"中的.cur光标样式文件, 这个工具只适用于Windows环境&#xff1b; 如果要提升其跨平台性的话&#xff0c;需要考虑替换cty…

【Deepseek私有化部署】解决 Anything LLM 上传文档一直转圈上传失败问题

这里写自定义目录标题 一、问题描述二、原因分析&#xff08;一&#xff09;Embedder 在 Anything LLM 中的核心作用&#xff08;二&#xff09;默认配置与 Deepseek 的适配问题&#xff08;三&#xff09;未正确配置 nomic - embed - text 引发的异常 三、解决途径&#xff08…

神经网络|(九)概率论基础知识-泊松分布及python仿真

【1】引言 在前序学习进程中&#xff0c;我们已经知晓二项分布是多重伯努利分布&#xff0c;二伯努利分布对应的是可以无限重复、结果只有两种可能的随机试验。 相关文章链接为&#xff1a; 神经网络|(八)概率论基础知识-二项分布及python仿真-CSDN博客 上述文章还调用nump…

《从0到1CTFer成长之路》逆向工程个人笔记--静态分析

上一篇文章&#xff1a;《从0到1CTFer成长之路》逆向工程个人笔记--逆向工程基础 IDA 使用入门 加载文件 打开 IDA&#xff0c;点击 GO&#xff0c;即可把程序拖拽到 IDA 中 IDA 分为 32bit 和 64bit 两种架构&#xff0c;选择哪种结构&#xff0c;可以在把程序拖拽到 IDA 后…