当前位置: 首页 > news >正文

责任链模式下,解决开闭原则问题实践

前言

在现代软件工程中,设计模式是解决常见问题的有效工具之一。它们吸收了前人的经验,不仅帮助开发者编写更清晰、更可维护的代码,还能促进团队之间的沟通和协作。责任链模式(Chain of Responsibility Pattern)作为一种常用的设计模式,广泛应用于多种场景,尤其适用于处理需要经过多个处理步骤的请求或命令。本文将从概念到具体实现,让你深刻理解责任链设计模式。

一、什么是责任链设计模式

责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许将请求沿着一个处理链传递,直到链中的某个对象处理它。这样,发送者无需知道哪个对象将处理请求,所有的处理对象都可以尝试处理请求或将请求传递给链上的下一个对象。总结来说,责任链模式实质上是一组链式调用的逻辑。

image-20241019114647704

在代码开发和维护过程中,随着系统复杂性的增加,原有的代码结构可能会变得难以维护。责任链模式正是为了解决这些问题而提出的。当代码中出现以下情形时,采用责任链设计模式进行重构便显得尤为重要:

  • 职责单一:责任链模式可以将每个验证逻辑封装到一个独立的处理器中,每个处理器负责单一的验证职责,符合单一职责原则。
  • 可扩展性:增加新的验证逻辑时,只需添加新的处理器,而不需要修改现有的代码。
  • 清晰的流程:将所有验证逻辑组织在一起,使得代码结构更加清晰,易于理解。

通过责任链模式,我们可以构建一个更加模块化、易于维护和扩展的系统架构。接下来,我们将详细介绍责任链模式的应用场景、优缺点以及具体的实现方法。

二、Java代码举例实现

现在我们采用Java实现一个过滤器调用的实现。总流程如下:

image-20241019120243192

  1. 定义过滤器接口和请求/响应类
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);}
}
  1. 执行过滤方法流程:
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);}}
}
  1. 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("过滤器链创建完毕");}
}
  1. 执行结果如下:
创建过滤器链
ConcreteFilterA 执行过滤
ConcreteFilterB 执行过滤
过滤器链创建完毕

通过前面的例子,我们可以看到,在手动实现责任链模式时,最大的问题在于 Client 类中需要手动添加过滤器。这种方式不仅增加了代码的复杂性,还不符合开闭原则(Open/Closed Principle, OCP),即软件实体应当对扩展开放,对修改关闭。

三、Spring环境解决开闭原则问题

为了解决这一问题,我们可以利用Spring上下文(ApplicationContext)来管理和获取Bean实例的

我们下面通过创建博客的例子,来在Spring环境下解决这个开闭原则问题。先说说它的具体执行流程:

image-20241019120440030

先来了解一下项目结构:

image-20241019131954509

这里的话,我们只给出核心类代码,具体代码可见:https://gitee.com/madaoEE/blog-chain

  1. 职责链接口:
public interface BlogCreateChainHandler <T> extends Ordered {/*** 执行责任链逻辑** @param requestParam 责任链执行入参*/void handler(T requestParam);/*** @return 责任链组件标识*/String mark();
}
  1. 职责链接口实现类
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;}
}

其他省略…

  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;}
}
  1. 具体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框架的依赖注入机制,我们解决了传统责任链实现中不符合开闭原则的问题,使得添加新的处理逻辑变得更加简单和灵活。

然而,值得注意的是,并非所有情况都适合应用责任链模式。在选择是否使用该模式时,我们需要考虑实际需求和场景特点。例如,在请求处理流程固定不变或者处理步骤较少的情况下,直接编码可能更为简洁有效。设计模式是一个工具,合理地根据实际情况选用合适的模式才是关键。对于责任链模式而言,它最适合于处理那些具有多层次决策逻辑的需求场景,能够有效地简化代码结构,提高系统的灵活性。

如果这篇文章对你有帮助,请点赞告诉我,这将是我继续分享的动力!感谢你的支持!

相关文章:

责任链模式下,解决开闭原则问题实践

前言 在现代软件工程中&#xff0c;设计模式是解决常见问题的有效工具之一。它们吸收了前人的经验&#xff0c;不仅帮助开发者编写更清晰、更可维护的代码&#xff0c;还能促进团队之间的沟通和协作。责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;作为一…...

对Android的Binder机制的了解

Android的Binder机制详解 Android的Binder机制是Android系统中用于进程间通信&#xff08;IPC&#xff09;的核心机制&#xff0c;它提供了一种高效、安全、稳定的进程间通信方式。以下将对Binder机制的基本概念、工作原理、应用场景、优势以及实现细节进行详细的阐述。 一、…...

收藏文章_VMware17Pro虚拟机安装教程(超详细)

收藏文章&#xff1a; VMware17Pro虚拟机安装教程(超详细) VMware虚拟机安装Linux教程(超详细)...

友思特分享 | 车载同步技术创新:多相机系统如何实现精准数据采集与实时处理?

导读 车载多相机采集系统是智能驾驶技术实际应用中的“眼睛”&#xff0c;友思特车载图像采集和回放系统切实提升了系统的实时同步采集与回放能力&#xff0c;为ADAS等应用的决策系统提供了可靠的核心数据。 视频流同步采集与智驾技术发展 在现代汽车行业中&#xff0c;智能驾…...

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&#xff1a;复杂的自定义边、边动画&#xff08;上&#xff09;_哔哩哔哩_bilibili AntV G6&#xff1a;复杂的自定义边、边动画&#xff08;下&#xff09;_哔哩哔哩_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 老旧版本免费用户的专业升级服务&#xff0c;为 GitLab 老旧版本进行专业升级&#xff0c;详情可以查看官网 GitLab 专业升级服务指南 今天&#xff0c;极狐GitLab 专业技术…...

MATLAB智能算法 - Immunity Algorithm免疫算法

Immunity Algorithm免疫算法 智能算法是路线规划、深度学习等等一系列领域所使用的优化算法&#xff0c;是算法进阶之路的必备之路。 前言&#xff1a;本文主要围绕解决TSP旅行商问题展开&#xff0c;对于机器人的路线规划以及非线性方程求解的问题等解决方案 对于一些其他智能…...

学习eNSP对提升就业竞争力有多大帮助?

学习eNSP&#xff08;Enterprise Network Simulation Platform&#xff09;对提升就业竞争力有显著帮助&#xff0c;具体表现在以下几个方面&#xff1a; 1. **增强专业技能**&#xff1a;通过eNSP&#xff0c;你可以模拟华为的网络设备&#xff0c;进行网络设计、配置和故障排…...

Molmo和PixMo:为最先进的多模态模型提供开放权重和开放数据

摘要 https://arxiv.org/pdf/2409.17146 当今最先进的多模态模型仍然是专有的。性能最强的开源模型严重依赖专有视觉语言模型(Vision-Language Model,简称VLM)的合成数据来获得良好性能,有效地将这些封闭模型提炼为开放模型。因此,业界仍然缺少关于如何从零开始构建高性能…...

day02_计算机常识丶第一个程序丶注释丶关键字丶标识符

计算机常识 计算机如何存储数据 计算机世界中只有二进制。那么在计算机中存储和运算的所有数据都要转为二进制。包括数字、字符、图片、声音、视频等。 进制 进制也就是进位计数制&#xff0c;是人为定义的带进位的计数方法 实例&#xff1a; // 在java 中 可以使用不同…...

【Trick】IOS系统解决“未受信任的企业级开发者”问题

问题&#xff1a; 本人通过扫码下载了一个软件&#xff0c;下载完毕后出现以下提示&#xff1a; 解决方法&#xff1a; 这个主要是操作系统的问题&#xff0c;需要在设置里面更改&#xff0c;具体步骤如下&#xff1a; 【1】打开设置&#xff0c;选择【通用】 【2】选择【VP…...

理解 React 中的 ReactElement、children 和 ReactNode

1. 什么是 ReactElement&#xff1f; ReactElement 是 React 用来描述 UI 界面元素的最基本的对象&#xff0c;是构建虚拟 DOM 的核心元素。 定义&#xff1a;ReactElement 是不可变的对象&#xff0c;表示界面中的某个元素。它包含了用于渲染 UI 所需的信息&#xff0c;如元…...

纯血鸿蒙正式登场,华为这新机给我看傻了

从 vivo 率先开炮 X200 系列&#xff0c;手机的白热化战斗序幕马上也就要揭开了。 就在昨天&#xff0c;骁龙于夏威夷召开骁龙峰会。 性能提升和咱们以往的爆料差距不大。 只是高通又双叒叕给自己改名了。新命名为 Snapdragon 8 Elite&#xff0c;官方翻译是骁龙 8 至尊版。 …...

c语言中的%运算和/运算

在C语言中&#xff0c;%运算和/运算分别表示取模运算和除法运算。以下是它们的详细解释和用法&#xff1a; 1. % 运算&#xff08;取模运算&#xff09; 取模运算用于计算两个整数相除后的余数。语法如下&#xff1a; result a % b; a 是被除数。b 是除数。result 是 a 除…...

【MySQL】多表查询——内连接,左/右连接

目录 准备工作 1.多表查询 2.INNER JOIN&#xff08;内连接&#xff09; 2.1.笛卡尔积 1.2.笛卡尔积的过滤 1.3.INNER JOIN&#xff08;显式内连接&#xff09; 1.4.SELF JOIN&#xff08;自连接&#xff09; 3. LEFT JOIN&#xff08;左连接&#xff09; 3.1.一个例子…...

Naicat连接本地CentOS 7虚拟机上的MySQL数据库失败解决办法

注意&#xff1a;Navicat主机栏填的是Centos虚拟机的IP地址 一、检查mysql容器 确保网络正常、保证mysql容器处于运行中且用户名、密码和端口正确。 1、查看mysql容器是否运行 docker ps2、查看mysql容器详细信息&#xff0c;可查看端口 docker inspect mysql二、检查防火墙…...

transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)的计算过程

cifar10数据集的众多demo中&#xff0c;在数据加载环节&#xff0c;transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)这条指令是经常看到的。这是一个 PyTorch 中用于图像数据标准化的函数调用&#xff0c;它将图像的每个通道的值进行标准化处理&…...

Excel表格如何修改“打开密码”,简单几步,轻松搞定

在保护Excel文件的安全性时&#xff0c;设置打开密码是常见且有效的方式。然而&#xff0c;有时我们需要修改已经设置的打开密码&#xff0c;以确保文件安全性或更新密码信息。今天小编来分享一下修改Excel文件打开密码的方法&#xff0c;操作简单&#xff0c;一起来看看吧&…...

pandas 数据分析实战

一、pandas常用数据类型 series&#xff0c;带标签的一维数组。类似于字典&#xff0c;但是键作为索引。 datatimeindex&#xff0c;时间序列。 dataframe&#xff0c;带标签且大小可变的二维表格结构。 panel&#xff0c;带标签且大小可变的三维数组。 1.一维数组与操…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

《Docker》架构

文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器&#xff0c;docker&#xff0c;镜像&#xff0c;k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...

保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!

目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...