09.责任链模式
09. 责任链模式
什么是责任链设计模式?
责任链设计模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许将请求沿着处理者对象组成的链进行传递,直到有一个处理者对象能够处理该请求为止。这种模式的目的是解耦请求的发送者和接收者,使得多个对象都有机会处理请求,从而增强了系统的灵活性。
责任链模式通常包含以下几个角色:
- 请求者(Client):发起请求的对象。
- 抽象处理者(Handler):定义一个处理请求的接口,通常包含一个方法用于处理请求,以及一个指向下一个处理者的引用。
- 具体处理者(Concrete Handler):实现抽象处理者接口的具体类,负责处理它所负责的请求,并决定是否将请求传递给链中的下一个处理者。
- 链(Chain):包含多个处理者对象,负责将请求沿着链传递。
责任链模式的工作原理如下:
-
请求者创建一个请求并将其发送给链的起始处理者。
-
每个处理者对象检查请求是否由自己处理。
-
- 如果能够处理,则处理请求并结束责任链。
- 如果不能处理,则将请求传递给链中的下一个处理者。
-
这个过程一直持续,直到请求被处理或传递到链的末端。
责任链模式的优点包括:
- 增强了系统的灵活性和可扩展性,因为可以动态地添加或移除处理者。
- 降低了对象之间的耦合度,因为发送者和接收者不需要直接交互。
- 允许多个对象处理同一个请求,增加了处理请求的灵活性。
责任链模式的缺点包括:
- 请求的传递路径可能难以跟踪,尤其是在链很长或处理者逻辑复杂的情况下。
- 责任链可能会导致系统性能问题,因为请求需要在多个对象之间传递。
责任链模式在实际应用中非常广泛,例如在GUI编程中处理事件、在网络编程中处理请求、在工作流系统中处理任务等场景。
举个简单的需求:
假如我们有个登录的场景,在登录处理流程中,需要校验参数、填充参数、登录判断、登录日志记录。我们每步都是环环相扣,此时就可以使用责任链模式。
有几个重要角色:
- **Action:**责任链中的一个执行动作,主要定义具体执行动作,以及是否需要跳过。
- **ActionChain:**责任链,用于定义添加执行动作方法,以及调度整条链路动作执行。
- **ActionContext:**执行动作上下文,定义一个上下文对象,用来在链路执行过程中存储和传输数据。
将上面的步骤看做一个一个执行动作,建立对应的action,使用 chain 将多个 action 进行串联,同时我们可以定义一个context 上下文,用来在各个action之间传输数据。
除此之外,我们也可以通过配置中心,来定义哪些步骤需要执行,哪些可以跳过。
类图如下:
代码编写:
1、定义顶级接口
(1)定义责任链执行动作上下文抽象类,用于责任链上下文之间数据传输。
@Data
public abstract class ActionContext implements Serializable, Cloneable {private static final long serialVersionUID = 1L;/*** 执行链名称,用于获取配置*/private String actionChainName;/*** 跳到结果处理*/private boolean isSkipToResult = false;public Object clone() throws CloneNotSupportedException {return super.clone();}
}
(2)定义责任链执行动作基类
public interface IAction<T extends ActionContext> {/*** 是否需要跳过* @param context 上下文* @return true/false*/default boolean isSkippered(T context) throws Exception{if (context.isSkipToResult()) {return true;}// 通过配置中心获取是否需要执行List<String> config = ConfigServer.getConfig(context.getActionChainName());if (config.contains(getName())) {return false;}return true;}/*** 执行* @param context 上下文*/void execute(T context) throws Exception;/*** 获取执行动作名称,用于和配置中心进行匹配* @return*/String getName();}
(3)定义Action 执行链接口
public interface IActionChain<T extends ActionContext> {/*** 添加一个Action* @param action 上下文* @return action链*/IActionChain<T> appendAction(Class<? extends IAction<T>> action);IActionChain<T> appendActions(List<Class<? extends IAction<T>>> actions);IActionChain<T> appendAction(IAction<T> action);/*** 执行动作* @param context 上下文*/void execute(T context) throws Exception;
}
2、实现接口,定义具体的执行
(1)登录上下文
/*** 登录上下文*/
@EqualsAndHashCode(callSuper = true)
@Data
public class LoginActionContext extends ActionContext {/*** 失败日志*/private String failMsg;private String userName;private String password;private String token;private String ip;private String device;private Boolean isLoginFlag = true;
}
(2)定义责任链通用模版类
public class RouteActionChain<T extends ActionContext> implements IActionChain<T> {private List<IAction<T>> actionChain = new ArrayList<IAction<T>>();@Overridepublic IActionChain<T> appendAction(Class<? extends IAction<T>> action) {actionChain.add(getActionInstance(action));return this;}@Overridepublic IActionChain<T> appendActions(List<Class<? extends IAction<T>>> actions) {if (CollectionUtils.isEmpty(actions)) {return this;}for (Class<? extends IAction<T>> clazz : actions) {actionChain.add(getActionInstance(clazz));}return this;}@Overridepublic IActionChain<T> appendAction(IAction<T> action) {actionChain.add(action);return this;}@Overridepublic void execute(T context) throws Exception {for (IAction<T> action : actionChain) {//如果跳过 就不需要继续执行,这里顺序不能改变if (action.isSkippered(context)) {continue;}action.execute(context);}}public static <T extends ActionContext> IAction<T> getActionInstance(Class<? extends IAction<T>> clazz) {Collection<? extends IAction<T>> s = BeanUtil.getBeans(clazz);if (s != null && s.size() == 1) {return s.iterator().next();} else {throw new RuntimeException("action is not found");}}
}
(3)定义执行动作
- 校验参数执行动作
@Slf4j@Componentpublic class CheckParamAction implements IAction<LoginActionContext> {@Overridepublic void execute(LoginActionContext context) {// do somethinglog.info("CheckParamAction execute......");// 使用断言,判断用户名不为空try {Assert.isTrue(!StringUtils.isEmpty(context.getUserName()), "用户名不能为空");Assert.isTrue(!StringUtils.isEmpty(context.getPassword()), "密码不能为空");} catch (Exception e) {context.setIsLoginFlag(false);context.setSkipToResult(true);context.setFailMsg(e.getMessage());}}@Overridepublic String getName() {return "CheckParamAction";}}
填充参数执行动作
@Slf4j@Componentpublic class FullParamAction implements IAction<LoginActionContext> {@Autowiredprivate ConfigServer configServer;@Overridepublic void execute(LoginActionContext context) throws Exception {log.info("FullParamAction execute......");// 使用断言,判断用户名不为空try {// do somethingcontext.setIp("127.0.0.1");context.setDevice("PC");context.setToken("123456");} catch (Exception e) {context.setIsLoginFlag(false);context.setSkipToResult(true);context.setFailMsg(e.getMessage());}}@Overridepublic String getName() {return "FullParamAction";}}
- 登录判断执行动作
@Slf4j@Componentpublic class LoginAction implements IAction<LoginActionContext> {@Overridepublic void execute(LoginActionContext context) throws Exception {// 模拟登录log.info("LoginAction execute......");try {// do somethingif(context.getUserName().equals(context.getPassword())) {context.setIsLoginFlag(true);} else {context.setIsLoginFlag(false);context.setFailMsg("用户名或密码输入错误");}} catch (Exception e) {context.setIsLoginFlag(false);context.setSkipToResult(true);context.setFailMsg(e.getMessage());}}@Overridepublic String getName() {return "LoginAction";}}
- 登录日志记录执行动作
@Slf4j
@Component
public class LogAction implements IAction<LoginActionContext> {@Overridepublic void execute(LoginActionContext context) throws Exception {log.info("FullParamAction execute......");// 使用断言,判断用户名不为空try {// do somethinglog.info("数据库插入登录日志:{}", JSONObject.toJSONString(context));} catch (Exception e) {context.setIsLoginFlag(false);context.setSkipToResult(true);context.setFailMsg(e.getMessage());}}@Overridepublic String getName() {return "LogAction";}
}
(4)模拟配置中心
配置需要的执行动作,没有配置的自动跳过
@Component
@Data
public class ConfigServer {private static Map<String, List<String>> configMap = new HashMap<>();@PostConstructpublic void init(){ArrayList<String> configList = new ArrayList<>();configList.add("CheckParamAction");configList.add("FullParamAction");configList.add("LogAction");configList.add("LoginAction");configMap.put("login", configList);}/*** 获取配置列表* @param actionChainName* @return*/public static List<String> getConfig(String actionChainName) {return configMap.getOrDefault(actionChainName, new ArrayList<>());}
}
3、测试
定义测试接口
@Service
public class LoginServiceImpl implements LoginService {@Overridepublic boolean login(String userName, String password) {// 创建上下文LoginActionContext loginActionContext = new LoginActionContext();loginActionContext.setActionChainName("login");loginActionContext.setUserName(userName);loginActionContext.setPassword(password);// 构建责任链RouteActionChain<LoginActionContext> chain = new RouteActionChain<>();chain.appendAction(CheckParamAction.class);chain.appendAction(FullParamAction.class);chain.appendAction(LoginAction.class);chain.appendAction(LogAction.class);try {chain.execute(loginActionContext);} catch (Exception e) {throw new RuntimeException(e);}return loginActionContext.getIsLoginFlag();}
}
(1)测试正常情况,传输正确的 username 和 password。
所有执行动作正常执行。
(2)测试异常情况, 传输错误的 username 和 password。
中间 LoginAction 执行失败,自动跳出责任链,后续执行动作未执行。
到此,一个简单的责任链设计模式的 demo 就已完成。
拓展点:
• 可以对接配置中心,动态定义不同业务逻辑中需要执行的动作。
• 可以将幂等性校验,添加到判断动作是否执行逻辑中。
相关文章:

09.责任链模式
09. 责任链模式 什么是责任链设计模式? 责任链设计模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许将请求沿着处理者对象组成的链进行传递,直到有一个处理者对象能够处理该请求为止。这种模式的目的…...

Amazon云计算AWS(一)
目录 一、基础存储架构Dynamo(一)Dynamo概况(二)Dynamo架构的主要技术 二、弹性计算云EC2(一)EC2的基本架构(二)EC2的关键技术(三)EC2的安全及容错机制 提供的…...

十_信号4-SIGCHLD信号
SIGCHLD信号 在学习进程控制的时候,使用wait和waitpid系统调用何以回收僵尸进程,父进程可以阻塞等待,也可以非阻塞等待,采用轮询的方式不停查询子进程是否退出。 采用阻塞式等待,父进程就被阻塞了,什么都干…...

HCIP的学习(27)
RSTP—802.1W—快速生成树协议 STP缺陷: 1、收敛速度慢----STP的算法是一种被动的算法,依赖于计时器来进行状态变化 2、链路利用率低 RSTP向下兼容STP协议。(STP不兼容RSTP) 改进点1—端口角色 802.1D协议---根端口、指定端口…...

6. MySQL 查询、去重、别名
文章目录 【 1. 数据表查询 SELECT 】1.1 查询表中所有字段使用 * 查询表的所有字段列出表的所有字段 1.2 查询表中指定的字段 【 2. 去重 DISTINCT 】【 3. 设置别名 AS 】3.1 为表指定别名3.2 为字段指定别名 【 5. 限制查询结果的条数 LIMIT 】5.1 指定初始位置5.2 不指定初…...

Oracle导出clob字段到csv
使用UTL_FILE ref: How to Export The Table with a CLOB Column Into a CSV File using UTL_FILE ?(Doc ID 1967617.1) --preapre data CREATE TABLE TESTCLOB(ID NUMBER, MYCLOB1 CLOB, MYCLOB2 CLOB ); INSERT INTO TESTCLOB(ID,MYCLOB1,MYCLOB2) VALUES(1,Sample row 11…...
C++无锁(lock free)队列moodycamel::ConcurrentQueue
moodycamel::ConcurrentQueue介绍 moodycamel::ConcurrentQueue一个用C++11实现的多生产者、多消费者无锁队列。 它具有以下特点: 1.快的让人大吃一惊,详见不同无锁队列之间的压测对比 2.单头文件实现,很容易集成到你的项目中 3.完全线程安全的无锁队列,支持任意线程数的并…...

python办公自动化——(二)替换PPT文档中图形数据-柱图
效果: 数据替换前 : 替换数据后: 实现代码 import collections.abc from pptx import Presentation from pptx.util import Cm,Pt import pyodbc import pandas as pd from pptx.chart.data impo…...

vue不同页面切换的方式(Vue动态组件)
v-if实现 <!--Calender.vue--> <template><a-calendar v-model:value"value" panelChange"onPanelChange" /></template> <script setup> import { ref } from vue; const value ref(); const onPanelChange (value, mod…...

Linux下Qt Creator无法输入中文(已解决)
1. 首先确保安装了搜狗输入法,且能正常运行。 2.克隆源码到本地。 git clone https://gitcode.com/fcitx/fcitx-qt5.git 3.检查Qt Creator版本,如下图所示,为基于Qt6的。 4. 进入源码目录,建立build文件夹,修改CMak…...

Codeforces 提交Java代码(自己处理输入输出)
示例一(A. Watermelon) 题目地址 Problem - 4A - Codeforces 题目截图 提交方式 可以提交本地文件,也可以在线提交。我们这里选择在线提交方式,点击上图中的 SUBMIT 按钮,会进入如下界面。 输入Java代码效果如下&a…...
剖析vue中nextTick源码
代码逻辑梳理: callbacks 数组用于存储待执行的回调函数,waiting 变量用于标记是否有待执行的回调函数。 flushCallbacks 函数用于执行所有存储在 callbacks 数组中的回调函数,并在执行完成后将 waiting 设置为 false。 timer 函数根据环境…...

SSM牙科诊所管理系统-计算机毕业设计源码98077
目 录 摘要 1 绪论 1.1研究目的与意义 1.2国内外研究现状 1.3ssm框架介绍 1.4论文结构与章节安排 2 牙科诊所管理系统系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能…...

【C++进阶】深入STL之string:模拟实现走进C++字符串的世界
📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C “ 登神长阶 ” 🤡往期回顾🤡:C模板入门 🌹🌹期待您的关注 🌹🌹 ❀STL之string 📒1. string…...

go语言linux安装
下载:https://go.dev/dl/ 命令行使用 wget https://dl.google.com/go/go1.19.3.linux-amd64.tar.gz解压下载的压缩包,linux建议放在/opt目录下 我放在/home/ihan/go_sdk下 sudo tar -C /home/ihan/go_sdk -xzf go1.19.3.linux-amd64.tar.gz 这里的参数…...
vi和vim有什么不同?
vi 和 vim 都是流行的文本编辑器,它们之间有以下主要区别: 历史: vi 是一个非常古老的文本编辑器,最初由 Bill Joy 在 1976 年为 Unix 系统编写。vim(Vi IMproved)是 vi 的一个增强版,由 Bram M…...
CSS动画效果(鼠标滑过按钮动画)
1.整体效果 https://mmbiz.qpic.cn/sz_mmbiz_gif/EGZdlrTDJa5SXiaicFfsrcric7TJmGO6YddqC4wFPdM7PGzPHuFgvtDS7MIvnLHB4WFaKia0Qh8VCyUaoyHMc2Zltg/640?wx_fmtgif&fromappmsg&tpwebp&wxfrom5&wx_lazy1&wx_co1 网页设计中的按钮不仅是用户交互的桥梁&#…...

数据结构(C):从初识堆到堆排序的实现
目录 🌞0.前言 🚈 1.堆的概念 🚈 2.堆的实现 🚝2.1堆向下调整算法 🚝2.2堆的创建(堆向下调整算法) ✈️2.2.1 向下调整建堆时间复杂度 🚝2.3堆向上调整算法 🚝2.…...

ChatGLM3-6B部署
ZhipuAI/chatglm3-6b 模型文件地址 chatglm3-6B-32k-int4 量化的模型地址 ChatGLM3 代码仓库 ChatGLM3 技术文档 cpolar http xxx 端口 /anaconda3/envs/chatglm2/lib/python3.8/site-packages/gradio$ networking.py 硬件环境 最低要求: 为…...
代码随想录35期Day54-JavaScript
Day54题目 ### LeetCode739每日温度 核心思想:今天主要是学会单调栈的使用.找到比元素更大的下一个元素,如果比栈顶元素小就入栈,否则就出栈顶元素,当前元素就是比栈顶元素大的"下一个更大的元素". /*** param {number[]} temperatures* return {number[]}*/ var …...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...

AI语音助手的Python实现
引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...
【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解
一、前言 在HarmonyOS 5的应用开发模型中,featureAbility是旧版FA模型(Feature Ability)的用法,Stage模型已采用全新的应用架构,推荐使用组件化的上下文获取方式,而非依赖featureAbility。 FA大概是API7之…...