责任链模式下,解决开闭原则问题实践
前言
在现代软件工程中,设计模式是解决常见问题的有效工具之一。它们吸收了前人的经验,不仅帮助开发者编写更清晰、更可维护的代码,还能促进团队之间的沟通和协作。责任链模式(Chain of Responsibility Pattern)作为一种常用的设计模式,广泛应用于多种场景,尤其适用于处理需要经过多个处理步骤的请求或命令。本文将从概念到具体实现,让你深刻理解责任链设计模式。
一、什么是责任链设计模式
责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许将请求沿着一个处理链传递,直到链中的某个对象处理它。这样,发送者无需知道哪个对象将处理请求,所有的处理对象都可以尝试处理请求或将请求传递给链上的下一个对象。总结来说,责任链模式实质上是一组链式调用的逻辑。
在代码开发和维护过程中,随着系统复杂性的增加,原有的代码结构可能会变得难以维护。责任链模式正是为了解决这些问题而提出的。当代码中出现以下情形时,采用责任链设计模式进行重构便显得尤为重要:
- 职责单一:责任链模式可以将每个验证逻辑封装到一个独立的处理器中,每个处理器负责单一的验证职责,符合单一职责原则。
- 可扩展性:增加新的验证逻辑时,只需添加新的处理器,而不需要修改现有的代码。
- 清晰的流程:将所有验证逻辑组织在一起,使得代码结构更加清晰,易于理解。
通过责任链模式,我们可以构建一个更加模块化、易于维护和扩展的系统架构。接下来,我们将详细介绍责任链模式的应用场景、优缺点以及具体的实现方法。
二、Java代码举例实现
现在我们采用Java实现一个过滤器调用的实现。总流程如下:
- 定义过滤器接口和请求/响应类
package com.example.provider.pattern.filter;/*** Filter 接口定义了过滤器的基本行为。* 每个具体的过滤器都需要实现此接口,并提供自己的 doFilter 实现。*/
public interface Filter {/*** 执行过滤操作。** @param request 当前请求对象* @param response 当前响应对象* @param chain 过滤链,用于调用链中的下一个过滤器*/void doFilter(Request request, Response response, FilterChain chain);
}/*** Response 类表示一个响应对象。* 包含与响应相关的属性和方法。*/
class Response {// 响应相关属性和方法
}/*** Request 类表示一个请求对象。* 包含与请求相关的属性和方法。*/
class Request {// 请求相关属性和方法
}/*** FilterChain 类表示一个过滤器链。* 它负责管理过滤器的顺序,并允许调用链中的下一个过滤器。*/
class FilterChain {// 过滤器链的相关属性和方法/*** 调用链中的下一个过滤器。*/public void doFilter() {// 实现调用链中的下一个过滤器的逻辑}
}
具体过滤器实现:
package com.example.provider.pattern.filter;public class ConcreteFilterA implements Filter {@Overridepublic void doFilter(Request request, Response response, FilterChain chain) {// 执行过滤操作ASystem.out.println("ConcreteFilterA 执行过滤");// 继续传递请求chain.doFilter(request, response);}
}public class ConcreteFilterB implements Filter {@Overridepublic void doFilter(Request request, Response response, FilterChain chain) {// 执行过滤操作BSystem.out.println("ConcreteFilterB 执行过滤");// 继续传递请求chain.doFilter(request, response);}
}
- 执行过滤方法流程:
package com.example.provider.pattern.filter;import java.util.ArrayList;
import java.util.List;/*** 过滤器链,用于依次执行添加到链中的过滤器。*/
public class FilterChain {private List<Filter> filters = new ArrayList<>();private int index = 0;public void addFilter(Filter filter) {filters.add(filter);}public void doFilter(Request request, Response response) {if (index < filters.size()) {Filter filter = filters.get(index++);filter.doFilter(request, response, this);}}
}
- Client端调用职责链方法
package com.example.provider.pattern.filter;public class Client {public static void main(String[] args) {// 创建过滤器Filter filterA = new ConcreteFilterA();Filter filterB = new ConcreteFilterB();System.out.println("创建过滤器链");// 创建过滤器链并添加过滤器FilterChain filterChain = new FilterChain();filterChain.addFilter(filterA);filterChain.addFilter(filterB);// 创建请求和响应对象Request request = new Request();Response response = new Response();// 通过过滤器链处理请求filterChain.doFilter(request, response);System.out.println("过滤器链创建完毕");}
}
- 执行结果如下:
创建过滤器链
ConcreteFilterA 执行过滤
ConcreteFilterB 执行过滤
过滤器链创建完毕
通过前面的例子,我们可以看到,在手动实现责任链模式时,最大的问题在于 Client
类中需要手动添加过滤器。这种方式不仅增加了代码的复杂性,还不符合开闭原则(Open/Closed Principle, OCP),即软件实体应当对扩展开放,对修改关闭。
三、Spring环境解决开闭原则问题
为了解决这一问题,我们可以利用Spring上下文(ApplicationContext)来管理和获取Bean实例的
我们下面通过创建博客的例子,来在Spring环境下解决这个开闭原则问题。先说说它的具体执行流程:
先来了解一下项目结构:

这里的话,我们只给出核心类代码,具体代码可见:https://gitee.com/madaoEE/blog-chain
- 职责链接口:
public interface BlogCreateChainHandler <T> extends Ordered {/*** 执行责任链逻辑** @param requestParam 责任链执行入参*/void handler(T requestParam);/*** @return 责任链组件标识*/String mark();
}
- 职责链接口实现类
package com.pxl.demo.service.handler;import com.pxl.demo.service.chain.BlogCreateChainHandler;
import org.springframework.stereotype.Component;/*** @author MADAO* @create 2024 - 10 - 19 13:00*/
@Component
public class BlogCreateNotNullChainFilter implements BlogCreateChainHandler {@Overridepublic void handler(Object requestParam) {System.out.println("博客创建参数非空判断");}@Overridepublic String mark() {return "blogCreate";}@Overridepublic int getOrder() {return 1;}
}
其他省略…
- 解决开闭原则核心类——Spring上下文
package com.pxl.demo.service.chain;import org.springframework.beans.BeansException;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import java.util.*;@Component
public final class MerchantAdminChainContext<T> implements ApplicationContextAware, CommandLineRunner {/*** 应用上下文,我们这里通过 Spring IOC 获取 Bean 实例*/private ApplicationContext applicationContext;private final Map<String, List<BlogCreateChainHandler>> abstractChainHandlerContainer = new HashMap<>();/*** 责任链组件执行** @param mark 责任链组件标识* @param requestParam 请求参数*/public void handler(String mark, T requestParam) {// 根据 mark 标识从责任链容器中获取一组责任链实现 Bean 集合List<BlogCreateChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(mark);if (CollectionUtils.isEmpty(abstractChainHandlers)) {throw new RuntimeException(String.format("[%s] Chain of Responsibility ID is undefined.", mark));}abstractChainHandlers.forEach(each -> each.handler(requestParam));}@Overridepublic void run(String... args) throws Exception {// 从 Spring IOC 容器中获取指定接口 Spring Bean 集合Map<String, BlogCreateChainHandler> chainFilterMap = applicationContext.getBeansOfType(BlogCreateChainHandler.class);chainFilterMap.forEach((beanName, bean) -> {// 判断 Mark 是否已经存在抽象责任链容器中,如果已经存在直接向集合新增;如果不存在,创建 Mark 和对应的集合List<BlogCreateChainHandler> abstractChainHandlers = abstractChainHandlerContainer.getOrDefault(bean.mark(), new ArrayList<>());abstractChainHandlers.add(bean);abstractChainHandlerContainer.put(bean.mark(), abstractChainHandlers);});abstractChainHandlerContainer.forEach((mark, unsortedChainHandlers) -> {// 对每个 Mark 对应的责任链实现类集合进行排序,优先级小的在前unsortedChainHandlers.sort(Comparator.comparing(Ordered::getOrder));});}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}
}
- 具体Service执行
package com.pxl.demo.service.impl;import com.pxl.demo.service.BlogService;
import com.pxl.demo.service.chain.MerchantAdminChainContext;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;/*** @author MADAO* @create 2024 - 10 - 19 12:58*/
@Service
@RequiredArgsConstructor
public class BlogServiceImpl implements BlogService {private final MerchantAdminChainContext merchantAdminChainContext;@Overridepublic String addBlog() {merchantAdminChainContext.handler("blogCreate",null);return "创建成功";}
}
调用接口,打印日志如下:http://localhost:8080/blog/add
博客创建参数非空判断
博客创建其他判断
博客创建审核判断
四、总结
本文通过详细的理论介绍和Java代码示例,展示了如何使用责任链设计模式来构建一个模块化的系统。责任链模式通过将请求沿处理链传递,允许系统内部以一种松耦合的方式处理请求,提高了系统的可扩展性和可维护性。同时,通过Spring框架的依赖注入机制,我们解决了传统责任链实现中不符合开闭原则的问题,使得添加新的处理逻辑变得更加简单和灵活。
然而,值得注意的是,并非所有情况都适合应用责任链模式。在选择是否使用该模式时,我们需要考虑实际需求和场景特点。例如,在请求处理流程固定不变或者处理步骤较少的情况下,直接编码可能更为简洁有效。设计模式是一个工具,合理地根据实际情况选用合适的模式才是关键。对于责任链模式而言,它最适合于处理那些具有多层次决策逻辑的需求场景,能够有效地简化代码结构,提高系统的灵活性。
如果这篇文章对你有帮助,请点赞告诉我,这将是我继续分享的动力!感谢你的支持!
相关文章:

责任链模式下,解决开闭原则问题实践
前言 在现代软件工程中,设计模式是解决常见问题的有效工具之一。它们吸收了前人的经验,不仅帮助开发者编写更清晰、更可维护的代码,还能促进团队之间的沟通和协作。责任链模式(Chain of Responsibility Pattern)作为一…...
对Android的Binder机制的了解
Android的Binder机制详解 Android的Binder机制是Android系统中用于进程间通信(IPC)的核心机制,它提供了一种高效、安全、稳定的进程间通信方式。以下将对Binder机制的基本概念、工作原理、应用场景、优势以及实现细节进行详细的阐述。 一、…...
收藏文章_VMware17Pro虚拟机安装教程(超详细)
收藏文章: VMware17Pro虚拟机安装教程(超详细) VMware虚拟机安装Linux教程(超详细)...

友思特分享 | 车载同步技术创新:多相机系统如何实现精准数据采集与实时处理?
导读 车载多相机采集系统是智能驾驶技术实际应用中的“眼睛”,友思特车载图像采集和回放系统切实提升了系统的实时同步采集与回放能力,为ADAS等应用的决策系统提供了可靠的核心数据。 视频流同步采集与智驾技术发展 在现代汽车行业中,智能驾…...
grafana failed to load dashboard from file= ... json error=EOF
问题描述 使用 prometheus-community/kube-prometheus-stack helm chart 部署 prometheus 监控后,查看 grafana pod 有如下报错 logger=provisioning.dashboard type=file name=default t=2024-10-17T06:30:47.937121541Z level=error msg...

【前端学习】AntV G6-09 复杂的自定义边、边动画
课程视频 AntV G6:复杂的自定义边、边动画(上)_哔哩哔哩_bilibili AntV G6:复杂的自定义边、边动画(下)_哔哩哔哩_bilibili 讲义截图 提及链接 https://codesandbox.io/p/sandbox/register-polyline-get…...
极狐GitLab 发布安全补丁版本 17.4.2, 17.3.5, 17.2.9
本分分享极狐GitLab 补丁版本 17.4.2, 17.3.5, 17.2.9 的详细内容。 极狐GitLab 正式推出面向 GitLab 老旧版本免费用户的专业升级服务,为 GitLab 老旧版本进行专业升级,详情可以查看官网 GitLab 专业升级服务指南 今天,极狐GitLab 专业技术…...

MATLAB智能算法 - Immunity Algorithm免疫算法
Immunity Algorithm免疫算法 智能算法是路线规划、深度学习等等一系列领域所使用的优化算法,是算法进阶之路的必备之路。 前言:本文主要围绕解决TSP旅行商问题展开,对于机器人的路线规划以及非线性方程求解的问题等解决方案 对于一些其他智能…...
学习eNSP对提升就业竞争力有多大帮助?
学习eNSP(Enterprise Network Simulation Platform)对提升就业竞争力有显著帮助,具体表现在以下几个方面: 1. **增强专业技能**:通过eNSP,你可以模拟华为的网络设备,进行网络设计、配置和故障排…...
Molmo和PixMo:为最先进的多模态模型提供开放权重和开放数据
摘要 https://arxiv.org/pdf/2409.17146 当今最先进的多模态模型仍然是专有的。性能最强的开源模型严重依赖专有视觉语言模型(Vision-Language Model,简称VLM)的合成数据来获得良好性能,有效地将这些封闭模型提炼为开放模型。因此,业界仍然缺少关于如何从零开始构建高性能…...

day02_计算机常识丶第一个程序丶注释丶关键字丶标识符
计算机常识 计算机如何存储数据 计算机世界中只有二进制。那么在计算机中存储和运算的所有数据都要转为二进制。包括数字、字符、图片、声音、视频等。 进制 进制也就是进位计数制,是人为定义的带进位的计数方法 实例: // 在java 中 可以使用不同…...

【Trick】IOS系统解决“未受信任的企业级开发者”问题
问题: 本人通过扫码下载了一个软件,下载完毕后出现以下提示: 解决方法: 这个主要是操作系统的问题,需要在设置里面更改,具体步骤如下: 【1】打开设置,选择【通用】 【2】选择【VP…...
理解 React 中的 ReactElement、children 和 ReactNode
1. 什么是 ReactElement? ReactElement 是 React 用来描述 UI 界面元素的最基本的对象,是构建虚拟 DOM 的核心元素。 定义:ReactElement 是不可变的对象,表示界面中的某个元素。它包含了用于渲染 UI 所需的信息,如元…...

纯血鸿蒙正式登场,华为这新机给我看傻了
从 vivo 率先开炮 X200 系列,手机的白热化战斗序幕马上也就要揭开了。 就在昨天,骁龙于夏威夷召开骁龙峰会。 性能提升和咱们以往的爆料差距不大。 只是高通又双叒叕给自己改名了。新命名为 Snapdragon 8 Elite,官方翻译是骁龙 8 至尊版。 …...
c语言中的%运算和/运算
在C语言中,%运算和/运算分别表示取模运算和除法运算。以下是它们的详细解释和用法: 1. % 运算(取模运算) 取模运算用于计算两个整数相除后的余数。语法如下: result a % b; a 是被除数。b 是除数。result 是 a 除…...

【MySQL】多表查询——内连接,左/右连接
目录 准备工作 1.多表查询 2.INNER JOIN(内连接) 2.1.笛卡尔积 1.2.笛卡尔积的过滤 1.3.INNER JOIN(显式内连接) 1.4.SELF JOIN(自连接) 3. LEFT JOIN(左连接) 3.1.一个例子…...

Naicat连接本地CentOS 7虚拟机上的MySQL数据库失败解决办法
注意:Navicat主机栏填的是Centos虚拟机的IP地址 一、检查mysql容器 确保网络正常、保证mysql容器处于运行中且用户名、密码和端口正确。 1、查看mysql容器是否运行 docker ps2、查看mysql容器详细信息,可查看端口 docker inspect mysql二、检查防火墙…...
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)的计算过程
cifar10数据集的众多demo中,在数据加载环节,transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)这条指令是经常看到的。这是一个 PyTorch 中用于图像数据标准化的函数调用,它将图像的每个通道的值进行标准化处理&…...

Excel表格如何修改“打开密码”,简单几步,轻松搞定
在保护Excel文件的安全性时,设置打开密码是常见且有效的方式。然而,有时我们需要修改已经设置的打开密码,以确保文件安全性或更新密码信息。今天小编来分享一下修改Excel文件打开密码的方法,操作简单,一起来看看吧&…...
pandas 数据分析实战
一、pandas常用数据类型 series,带标签的一维数组。类似于字典,但是键作为索引。 datatimeindex,时间序列。 dataframe,带标签且大小可变的二维表格结构。 panel,带标签且大小可变的三维数组。 1.一维数组与操…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...

基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器
一、原理介绍 传统滑模观测器采用如下结构: 传统SMO中LPF会带来相位延迟和幅值衰减,并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF),可以去除高次谐波,并且不用相位补偿就可以获得一个误差较小的转子位…...

在Zenodo下载文件 用到googlecolab googledrive
方法:Figshare/Zenodo上的数据/文件下载不下来?尝试利用Google Colab :https://zhuanlan.zhihu.com/p/1898503078782674027 参考: 通过Colab&谷歌云下载Figshare数据,超级实用!!࿰…...

【记录坑点问题】IDEA运行:maven-resources-production:XX: OOM: Java heap space
问题:IDEA出现maven-resources-production:operation-service: java.lang.OutOfMemoryError: Java heap space 解决方案:将编译的堆内存增加一点 位置:设置setting-》构建菜单build-》编译器Complier...

NoSQL——Redis配置与优化
目录 关系型&非关系型数据库 一、核心原理对比 二、核心特性对比 三、关键区别剖析 四、典型产品示例 总结 Redis Redis核心原理 核心特性 技术意义 配置文件解析 1. 基础配置 2. 持久化配置 3. 内存管理 4. 高可用配置 5. 性能调优 6.…...
Steam爬取相关游戏评测
## 因为是第一次爬取Steam。所以作为一次记录发出;有所错误欢迎指出。 无时间指定爬取 import requests import time import csv import osappid "553850" # 这里你也可以改成 #appid int(input()) max_reviews 10000 # 想爬多少条 # max_reviews…...