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

重修设计模式-行为型-责任链模式

重修设计模式-行为型-责任链模式

将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。

责任链模式(Chain of Responsibility Pattern)也叫职责链模式,通过将请求的处理分配给一系列的处理对象,这些处理对象通过链式结构组织起来,以实现对请求的灵活处理。比如,一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条,链条上的每个处理器承担各自的处理职责。

责任链模式的原理非常简单,有两种常用的实现。一种是使用链表来存储处理器,另一种是使用数组来存储处理器,后面一种实现方式更加简单。

1.链表方式

使用链表结构存储处理器,如果有处理器能处理该请求,就不继续往下传递;如果不能处理,则交由后面的处理器来处理。

//处理器
abstract class Handler {var next: Handler? = nullabstract fun handle()
}class Handler1: Handler() {override fun handle() {var handled = false//doAction...handled = Random.nextBoolean()println("${javaClass.simpleName}处理逻辑... 处理结果:$handled")//...if (!handled && next != null) {  //如果处理不了,交给下一个处理器next?.handle()}}
}class Handler2: Handler() {override fun handle() {var handled = false//doAction...handled = Random.nextBoolean()println("${javaClass.simpleName}处理逻辑... 处理结果:$handled")//...if (!handled && next != null) {next?.handle()}}
}//处理器链
class HandlerChain {var head: Handler? = nullvar tail: Handler? = nullfun addHandle(handler: Handler) {handler.next = nullif (head == null) {head = handlertail = handlerreturn}tail?.next = handlertail = handler}fun handle() {head?.handle()}
}//使用时:
fun main() {val chain = HandlerChain()chain.addHandle(Handler1())chain.addHandle(Handler2())chain.handle()  //开始责任链调用
}

2.集合方式

使用集合存储所有处理器,调用时通过遍历来串联所有处理器,实现更加简单

//处理器
abstract class HandlerList {abstract fun handle(): Boolean
}class HandlerA : HandlerList() {override fun handle(): Boolean {var handled = false//doAction...handled = Random.nextBoolean()println("${javaClass.simpleName}处理逻辑... 处理结果:$handled")//...//如果处理不了,交给下一个处理器return handled}
}class HandlerB : HandlerList() {override fun handle(): Boolean {var handled = false//doAction...handled = Random.nextBoolean()println("${javaClass.simpleName}处理逻辑... 处理结果:$handled")//...//如果处理不了,交给下一个处理器return handled}
}//处理器链
class HandlerChainB {private val chainList = mutableListOf<HandlerList>()fun addHandle(handler: HandlerList) {chainList.add(handler)}fun handle() {for (filter in chainList) {if (!filter.handle()) {return}}}
}//调用处:
fun main() {val chainB = HandlerChainB()chainB.addHandle(HandlerA())chainB.addHandle(HandlerB())chainB.handle()
}

责任链模式可以将请求和处理分开,请求者无需知道处理流程,只关注处理结果;处理者只关注处理逻辑。两者解耦,提高了系统的灵活性和可扩展性。利用它们来提供框架的扩展点,能够让框架的使用者在不修改框架源码的情况下,基于扩展点定制化框架的功能。

传统的责任链定义是,如果处理器链上的某个处理器能够处理这个请求,那就不会继续往下传递请求。实际上,职责链模式还有一种变体,那就是请求会被所有的处理器都处理一遍,不存在中途终止的情况。比如责任链在网络请求的应用:Okhttp

责任链模式在 Okhttp 中的运用

Okhttp 是移动端网络请求的框架,它内部的网络请求实现就用到了责任链模式。具体实现方式是通过集合存储不同职责的拦截器,保证拦截器执行顺序,并通过递归调用的方式将每个拦截器串联起来,从而支持双向拦截:既能拦截客户端发送的请求,也能拦截服务器返回的响应。

getResponseWithInterceptorChain 是 Okhttp 进行网络请求,获取服务端响应结果的核心方法,会通过几个不同职责的拦截器拿到数据,源码如下:

Response getResponseWithInterceptorChain() throws IOException {//装载不同职责的拦截器List<Interceptor> interceptors = new ArrayList<>();interceptors.addAll(client.interceptors());  //1.这是为客户端预留的拦截器扩展,可以拿到纯净的请求interceptors.add(retryAndFollowUpInterceptor); //2.重试和重定向拦截器interceptors.add(new BridgeInterceptor(client.cookieJar()));  //3.处理请求和响应的Header,如Content-Lengthinterceptors.add(new CacheInterceptor(client.internalCache())); //4.缓存拦截器,如果有缓存则直接返回缓存数据interceptors.add(new ConnectInterceptor(client));  //5.构建好TCP链接,确定好编/解码器的拦截器if (!forWebSocket) {interceptors.addAll(client.networkInterceptors()); //6.也是为客户端预留的拦截器扩展,可以拿到纯净的响应}interceptors.add(new CallServerInterceptor(forWebSocket)); //7.负责访问服务器并拿到响应结果Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,originalRequest, this, eventListener, client.connectTimeoutMillis(),client.readTimeoutMillis(), client.writeTimeoutMillis());Response response = chain.proceed(originalRequest);  //8.开始拦截器链式调用if (retryAndFollowUpInterceptor.isCanceled()) {closeQuietly(response);throw new IOException("Canceled");}return response;
}

拦截器接口定义:

public interface Interceptor {Response intercept(Chain chain) throws IOException;interface Chain {Request request();Response proceed(Request request) throws IOException;//...}
}

具体实现:

public final class RealInterceptorChain implements Interceptor.Chain {private final List<Interceptor> interceptors;private final int index;public RealInterceptorChain(List<Interceptor> interceptors, int index) {this.interceptors = interceptors;this.index = index;}@Override public Response proceed(Request request) throws IOException {if (index >= interceptors.size()) throw new AssertionError();// Call the next interceptor in the chain.RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,writeTimeout);Interceptor interceptor = interceptors.get(index);Response response = interceptor.intercept(next); //自定义拦截器需要调用chain.proceed继续责任链的调用,通过递归方式穿起来了//...return response;}
}

每个拦截器在内部会封装成 RealInterceptorChain 类型,它内部封装了网络的一些通用逻辑,这个可以忽略,内部还是调用了 intercept 方法传递给下一个拦截器,拦截器中又调用 chain.proceed 继续传递,通过递归的方式将拦截器链串联起来,达到双向拦截的效果,例如下面自定义的拦截器:

public final class CustomInterceptor implements Interceptor {@Override public Response intercept(Chain chain) throws IOException {//1.对请求进行处理,比如进行gzip压缩Response networkResponse = chain.proceed(requestBuilder.build());//2.对响应进行处理,比如gzip解压return networkResponse;}
}

总结

责任链模式是一种非常有用的设计模式,它通过链式结构来组织处理者,实现了请求的灵活处理和系统的可扩展性。

相关文章:

重修设计模式-行为型-责任链模式

重修设计模式-行为型-责任链模式 将请求的发送和接收解耦&#xff0c;让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链&#xff0c;并沿着这条链传递这个请求&#xff0c;直到链上的某个接收对象能够处理它为止。 责任链模式&#xff08;Chain of Responsibilit…...

Vercel部署/前端部署

Vercel 部署 今天要讲的是如何对别人向自己的开源仓库提的PR进行自动代码审核 1. 注册并登录Vercel 访问 Vercel官网点击右上角的"Sign Up"选择使用GitHub、GitLab、Bitbucket或邮箱注册完成注册流程并登录 2. 连接代码仓库 在Vercel仪表板,点击"New Proje…...

常见的css预处理器

CSS预处理器是一种扩展了CSS功能的脚本语言&#xff0c;它允许开发者以编程的方式编写更加干净、结构化的CSS代码。通过引入变量、嵌套规则、混合&#xff08;Mixins&#xff09;、函数等高级特性&#xff0c;CSS预处理器使得CSS代码的编写更加灵活、高效&#xff0c;同时也提高…...

mysql—半同步模式

mysql的并行复制 在172.25.254.20(slave)主机上 默认情况下slave中使用的是sql单线程回放 在master中时多用户读写&#xff0c;如果使用sql单线程回放那么会造成组从延迟严重 开启MySQL的多线程回放可以解决上述问题 mysql> show processlist; 在配置文件中进行编辑 [root…...

You are not allowed to push code to this project

原因1 用户权限不够。 具体查看用户权限路径&#xff1a; 原因2 vscode之前都能提交代码&#xff0c;但是突然就提交不上了。 表现为:前端代码能拉取&#xff0c;但是不能提交。使用idea进行前端代码的提交&#xff0c;完全没问题。 解决方案&#xff1a;修改TortoiseG…...

Java刷题:最小k个数

目录 题目描述&#xff1a; 思路&#xff1a; 具体实现 整体建立一个大小为N的小根堆 通过大根堆实现 完整代码 力扣链接&#xff1a;面试题 17.14. 最小K个数 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 设计一个算法&#xff0c;找出数组中最小的…...

Redis实战--Redis应用过程中出现的热门问题及其解决方案

Redis作为一种高性能的key-value数据库&#xff0c;广泛应用于缓存、消息队列、排行榜等场景。然而&#xff0c;在实际应用中&#xff0c;随着业务规模的不断扩大和访问量的持续增长&#xff0c;缓存系统也面临着诸多挑战&#xff0c;其中最为典型的便是缓存穿透、缓存击穿和缓…...

实时数字人DH_live使用案例

参看: https://github.com/kleinlee/DH_live ubuntu 测试 apt install ffmpeg 下载安装: git clone https://github.com/kleinlee/DH_live.git cd DH_liveconda create -n dh_live python=3.12 conda activate dh_live pip install -r requirements.txt pip install torch -…...

线上环境排故思路与方法GC优化策略

前言 这是针对于我之前[博客]的一次整理&#xff0c;因为公司需要一些技术文档的定期整理与分享&#xff0c;我就整理了一下。(https://blog.csdn.net/TT_4419/article/details/141997617?spm1001.2014.3001.5501) 其实&#xff0c;nginx配置 服务故障转移与自动恢复也是可以…...

硬件设计很简单?合宙低功耗4G模组Air780E—开机启动及外围电路设计

Air780E是合宙低功耗4G-Cat.1模组经典型号之一&#xff0c;上期我们解答了大家关心的系列问题&#xff0c;并讲解了选型的注意要点。 有朋友问&#xff1a;能不能讲些硬件设计相关的内容&#xff1f; 模组的上电开机&#xff0c;是硬件设计调试的第一步。 本期特别分享——Ai…...

初试AngularJS前端框架

文章目录 一、框架概述二、实例演示&#xff08;一&#xff09;创建网页&#xff08;二&#xff09;编写代码&#xff08;三&#xff09;浏览网页&#xff08;四&#xff09;运行结果 三、实战小结 一、框架概述 AngularJS 是一个由 Google 维护的开源前端 JavaScript 框架&am…...

【学习笔记】手写 Tomcat 六

目录 一、线程池 1. 构建线程池的类 2. 创建任务 3. 执行任务 测试 二、URL编码 解决方案 测试 三、如何接收客户端发送的全部信息 解决方案 测试 四、作业 1. 了解工厂模式 2. 了解反射技术 一、线程池 昨天使用了数据库连接池&#xff0c;我们了解了连接池的优…...

打靶记录18——narak

靶机: https://download.vulnhub.com/ha/narak.ova 推荐使用 VM Ware 打开靶机 难度&#xff1a;中 目标&#xff1a;取得 root 权限 2 Flag 攻击方法&#xff1a; 主机发现端口扫描信息收集密码字典定制爆破密码Webdav 漏洞PUT 方法上传BF 语言解码MOTD 注入CVE-2021-3…...

LabVIEW编程能力如何能突飞猛进

要想让LabVIEW编程能力实现突飞猛进&#xff0c;需要采取系统化的学习方法&#xff0c;并结合实际项目进行不断的实践。以下是一些提高LabVIEW编程能力的关键策略&#xff1a; 1. 扎实掌握基础 LabVIEW的编程本质与其他编程语言不同&#xff0c;它是基于图形化的编程方式&…...

代码随想录算法训练营第四四天| 1143.最长公共子序列 1035.不相交的线 53. 最大子序和 392.判断子序列

今日任务 1143.最长公共子序列 1035.不相交的线 53. 最大子序和 392.判断子序列 1143.最长公共子序列 题目链接&#xff1a; . - 力扣&#xff08;LeetCode&#xff09; class Solution {public int longestCommonSubsequence(String text1, String text2) {int[][] dp ne…...

2024.9.26 作业 +思维导图

一、作业 1、什么是虚函数&#xff1f;什么是纯虚函数 虚函数&#xff1a;函数前加关键字virtual&#xff0c;就定义为虚函数&#xff0c;虚函数能够被子类中相同函数名的函数重写 纯虚函数&#xff1a;把虚函数的函数体去掉然后加0&#xff1b;就能定义出一个纯虚函数。 2、基…...

WSL进阶体验:gnome-terminal启动指南与中文显示问题一网打尽

起因 我们都知道 wsl 启动后就死一个纯命令行终端&#xff0c;一直以来我都是使用纯命令行工具管理Linux的。今天看到网上有人在 wsl 中启动带图形界面的软件。没错&#xff0c;就是在wsl中启动带有图形界面的Linux软件。比如下面这个编辑器。 ​​ 出于好奇&#xff0c;我就…...

recoil和redux之间的选择

Recoil 和 Redux 是两个流行的 JavaScript 状态管理库&#xff0c;它们各自有不同的设计理念和使用场景。选择哪一个更好用&#xff0c;取决于你的具体需求、项目规模和个人偏好。 1. 设计理念 Redux 单向数据流&#xff1a;Redux 采用单向数据流模型&#xff0c;所有的状态变…...

无人机的作战指挥中心-地面站!

无人机与地面站的关系 指挥与控制&#xff1a;地面站是无人机系统的核心控制部分&#xff0c;负责对无人机进行远程指挥和控制。无人机根据地面站下达的任务自主完成飞行任务&#xff0c;并实时向地面站反馈飞行状态和任务执行情况。 任务规划与执行&#xff1a;地面站具备任…...

Vue 23进阶面试题:(第八天)

目录 29.vue2.0和vue3.0区别&#xff1f; 30.事件中心的原理 31.使用基于token的登录流程 32.防抖和节流 防抖&#xff08;debounce&#xff09; 节流&#xff08;throttle&#xff09; 29.vue2.0和vue3.0区别&#xff1f; 1.由选项API转变为组合API。 2.vue3将全局配置…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...