设计模式之责任链模式

简介

责任链模式是一种行为设计模式, 允许你将请求沿着处理者链(单向链表)进行发送。 收到请求后, 每个处理者根据自身条件对请求进行处理, ,如果处理不了则将其传递给链上的下个处理者,以此类推,直到有处理着能对请求进行处理返回,当到达处理者链的最后一个节点也不能处理该请求,则抛出异常。
可以用下图表示责任链设计模式:
在这里插入图片描述

为什么用责任链

按照简介,可以用if-else来代替:

if(条件1){//doSomething
}else if(条件2){//doSomething
}else if(条件3){//doSomething
}else{//doSomething
}

当条件很多的时候,if-else就变得很多很长,看起来头疼,维护起来也头疼,代码耦合度很高,当需要新增或删除条件的时候,维护成本就很高。责任链的出现就是为了解耦。只需将请求放到处理者链上,处理者链对于请求会有各自的处理方式,更方便二次开发。

责任链结构

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

代码实现

抽象处理者(Handler)角色

public abstract class Handler {Handler next;/*** 使用Boolean类型而不用boolean是为了null这个值,当处理的结果是false时,* 但这个false是对业务有意义的,应该返回。但需要区分出是false还是找不到处理器,此时null就成了有意义的错误值* 用于区分是否找到对应的处理器* @param request 请求对象*/public abstract Boolean execute(RequestDemo request);public void setNext(Handler next) {this.next = next;}
}

具体处理者(Concrete Handler)角色

处理者A

public class ExecuteA extends Handler {@Overridepublic Boolean execute(RequestDemo request) {//判断是否由当前处理者处理if ("A".equals(request.getType())){System.out.println("A 处理");return true;}else if (next != null){//无法处理则传递给下一个处理者return next.execute(request);}else{//当到达最后一个处理者(最后一个处理者的next为null)还不能处理时,抛出异常(或进行其他处理)throw new RuntimeException("can not find any execute");}}
}

处理者B

public class ExecuteB extends Handler {@Overridepublic Boolean execute(RequestDemo request) {//判断是否由当前处理者处理if ("B".equals(request.getType())){System.out.println("B 处理");return true;}else if (next != null){//无法处理则传递给下一个处理者return next.execute(request);}else{//当到达最后一个处理者(最后一个处理者的next为null)还不能处理时,抛出异常(或进行其他处理)throw new RuntimeException("can not find any execute");}}
}

处理者C

public class ExecuteC extends Handler {@Overridepublic Boolean execute(RequestDemo request) {//判断是否由当前处理者处理if ("C".equals(request.getType())){System.out.println("C 处理");return true;}else if (next != null){//无法处理则传递给下一个处理者return next.execute(request);}else{//当到达最后一个处理者(最后一个处理者的next为null)还不能处理时,抛出异常(或进行其他处理)throw new RuntimeException("can not find any execute");}}
}

处理传递的对象(额外)

public class RequestDemo {private String type;public RequestDemo(String type) {this.type = type;}public String getType() {return type;}public void setType(String type) {this.type = type;}
}

客户类(Client)角色

public class NormMain {public static void main(String[] args) {ExecuteA executeA = new ExecuteA();ExecuteB executeB = new ExecuteB();ExecuteC executeC = new ExecuteC();//构建责任链,即构建处理的链表executeA.setNext(executeB);executeB.setNext(executeC);//构建需要处理对象RequestDemo requestDemo = new RequestDemo("C");//责任链的第一个节点开始调用,保证处理是层层往下的,不会忽略某个节点的处理器executeA.execute(requestDemo);}
}

优化版责任链模式

上面的责任链构建方式看起来有点冗余,可以优化一下。在抽象处理者里定义一个有序列表保存所有的处理者,当需要处理请求的时候,对列表进行遍历挑选合适的处理者即可。

抽象处理者(Handler)角色

public abstract class Handler {Handler next;/*** 使用Boolean类型而不用boolean是为了null这个值,当处理的结果是false时,* 但这个false是对业务有意义的,应该返回。但需要区分出是false还是找不到处理器,此时null就成了有意义的错误值* 用于区分是否找到对应的处理器* @param request 请求对象*/public abstract Boolean execute(RequestDemo request);public void setNext(Handler next) {this.next = next;}
}

执行链表对象:

public class HandlerChain {private List<Handler> handlers = new ArrayList<>();public HandlerChain(List<Handler> handlers) {this.handlers = handlers;}public HandlerChain(Handler ...handler) {handlers.addAll(Arrays.asList(handler));}public void addHandler(Handler handler){handlers.add(handler);}public void addHandlers(Handler ...handler){handlers.addAll(Arrays.asList(handler));}public void addHandler(List<List> handler){this.handlers = handlers;}public boolean handleRequest(RequestDemo request) {if (handlers == null || handlers.size() == 0){throw new RuntimeException("can not find any execute");}for (Handler handler : handlers) {Boolean executeResult = handler.execute(request);if (executeResult != null){//找到对应的处理器,处理后应该返回return executeResult ;}}// 如果执行到这里,说明上面的return并没有执行,也就是找不到对应的处理器,此时该抛出异常throw new RuntimeException("can not find any execute");}
}

处理者A

public class ExecuteA extends Handler {@Overridepublic Boolean execute(RequestDemo request) {if ("A".equals(request.getType())){System.out.println("A 处理");return true;}return null;}
}

处理者B

public class ExecuteB extends Handler {@Overridepublic Boolean execute(RequestDemo request) {if ("B".equals(request.getType())){System.out.println("B 处理");return true;}return null;}
}

处理者C

public class ExecuteC extends Handler {@Overridepublic Boolean execute(RequestDemo request) {if ("C".equals(request.getType())){System.out.println("C 处理");return true;}return null;}
}

客户类(Client)角色

public class ListHandlerMain {public static void main(String[] args) {RequestDemo requestDemo = new RequestDemo("C");HandlerChain handlerChain = new HandlerChain(new ExecuteA(),new ExecuteB(),new ExecuteC());handlerChain.handleRequest(requestDemo);}

优点:使用这种含列表方式的责任链的话,不需要调用抽象类的setNext方法指定下一个执行对象,只需要按顺序放入列表中即可,因为链表已经按顺序执行了,代码变得很简洁。

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

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

相关文章

【LVS】3、LVS+Keepalived群集

为什么用它&#xff0c;为了做高可用 服务功能 1.故障自动切换 2.健康检查 3.节点服务器高可用-HA Keepalived的三个模块&#xff1a; core&#xff1a;Keepalived的核心&#xff0c;负责主进程的启动、维护&#xff1b;调用全局配置文件进行加载和解析 vrrp&#xff1a;实…

5个高清视频素材网站

推荐5个高清视频素材网站&#xff0c;免费、付费、商用的都有&#xff0c;可根据自己需求去选择&#xff0c;赶紧收藏吧&#xff01; 菜鸟图库 https://www.sucai999.com/video.html?vNTYxMjky ​ 菜鸟图库网素材非常丰富&#xff0c;网站主要还是以设计类素材为主&#xff…

Spring-Bean的生命周期

目录 生命周期汇总 细分生命周期 1.实例化 2.属性赋值&#xff08;依赖注入&#xff09; 3.Aware接口 4.BeanPostProcessor接口 5.初始化 6.销毁 测试验证 类结构 业务类 测试类 生命周期汇总 Spring Bean 的生命周期见下图 &#xff08;一定记忆好下图&#x…

二进制数的左移和右移位运算numpy.left_shift()numpy.right_shift()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 二进制数的左移和右移位运算 numpy.left_shift() numpy.right_shift() [太阳]选择题 下列代码最后一次输出的结果是&#xff1f; import numpy as np a 8 print("【显示】a ", a)…

【编织时空四:探究顺序表与链表的数据之旅】

本章重点 链表的分类 带头双向循环链表接口实现 顺序表和链表的区别 缓存利用率参考存储体系结构 以及 局部原理性。 一、链表的分类 实际中链表的结构非常多样&#xff0c;以下情况组合起来就有8种链表结构&#xff1a; 1. 单向或者双向 2. 带头或者不带头 3. 循环或者非…

CANoe软件Tools中无法找到LDF Explorer

关联文章&#xff1a; LDF概述和LDF Explorer工具介绍 问题描述 使用CANoe软件的菜单栏Tools中无法找到LDF Explorer 原因分析/解决方案&#xff1a; ①查看CANoe硬件是否带LIN license&#xff0c;并且license在正常激活时间内。 ②查看CANoe是否配置了LIN通道&#xff0c;…

【链表】经典链表题LeetCode

文章目录 160. 相交链表 简单&#x1f525;206. 反转链表 简单&#x1f525;876. 链表的中间结点 简单234. 回文链表 简单&#x1f525;141. 环形链表 简单&#x1f525;142. 环形链表 II 中等&#x1f525;21. 合并两个有序链表 简单&#x1f525;2. 两数相加 中等&#x1f52…

【Unity】ShaderGraph应用(模型膨胀流动)

【Unity】ShaderGraph应用&#xff08;模型膨胀流动&#xff09; 实现效果 ShaderGraph是 unity的图形化 Shader 编程工具。本文介绍使用ShaderGraph实现模型的膨胀流动效果。该效果可以由于模拟流体在管线中的流动等相关功能。 一、实现的方法 1.使用节点介绍 关键节点 UV…

【Redis实践篇】使用Redisson 优雅实现项目实践过程中的5种场景

文章目录 1.前言2.使用方式1. 添加Redisson依赖&#xff1a;2. 配置Redis连接信息3. 使用场景3.1. 分布式锁3.2. 限流器&#xff08;Rate Limiter&#xff09;3.3. 可过期的对象&#xff08;Expirable Object&#xff09;3.4. 信号量&#xff08;Semaphore&#xff09;3.5. 分布…

首起针对国内金融企业的开源组件投毒攻击事件

简述 2023年8月9日&#xff0c;墨菲监控到用户名为 snugglejack_org (邮件地址&#xff1a;SnuggleBearrxxhotmail.com&#xff09;的用户发布到 NPM 仓库中的 ws-paso-jssdk 组件包具有发向 https://ql.rustdesk[.]net 的可疑流量&#xff0c;经过确认该组件包携带远控脚本&a…

PHP 从 URL(链接) 字符串中获取参数

PHP 从 URL&#xff08;链接&#xff09; 字符串中获取参数 //URL(链接)字符串 $url https://www.baidu.com/?name小洪帽i&sex男&age999; //parse_url 函数从一个 URL 字符串中获取参数 $urlparse_url($url); //输出获取到的内容 echo "<pre>"; pri…

PyTorch学习笔记(十三)——现有网络模型的使用及修改

以分类模型的VGG为例 vgg16_false torchvision.models.vgg16(weightsFalse) vgg16_true torchvision.models.vgg16(weightsTrue) 设置为 False 的情况&#xff0c;相当于网络模型中的参数都是初始化的、默认的设置为 True 时&#xff0c;网络模型中的参数在数据集上是训练好…

WSL2 ubuntu子系统OpenCV调用本机摄像头的RTSP视频流做开发测试

文章目录 前言一、Ubuntu安装opencv库二、启动 Windows 本机的 RTSP 视频流下载解压 EasyDarwin查看本机摄像头设备开始推流 三、在ubuntu 终端编写代码创建目录及文件创建CMakeLists.txt文件启动 cmake 配置并构建 四、结果展示启动图形界面在图形界面打开终端找到 rtsp_demo运…

汽车租赁管理系统/汽车租赁网站的设计与实现

摘 要 租赁汽车走进社区&#xff0c;走进生活&#xff0c;成为当今生活中不可缺少的一部分。随着汽车租赁业的发展&#xff0c;加强管理和规范管理司促进汽车租赁业健康发展的重要推动力。汽车租赁业为道路运输车辆一种新的融资服务形式、广大人民群众一种新的出行消费方式和…

VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)

本例主要讲一下&#xff0c;用VC6如何生成DLL&#xff0c;用工具WinHex取得DLL全部内容&#xff0c;VC2015项目加载内存中的DLL函数&#xff0c;并调用函数的示例。 本例中的示例代码下载&#xff0c;点击可以下载 一、VC6.0生成示例DLL项目 1.新建项目&#xff0c;…

mac 可以进行单片机(stm32)的开发吗?

当涉及到在Mac上进行单片机开发时&#xff0c;是完全可行的。以下是为什么Mac适合单片机开发的解释&#xff1a;开发工具&#xff1a;针对STM32单片机&#xff0c;你可以使用多种开发工具。一个常用的选择是Segger Embedded Studio&#xff0c;它是一个功能强大的集成开发环境&…

ONNX版本YOLOV5-DeepSort (rknn版本已经Ready)

目录 1. 前言 2. 储备知识 3. 准备工作 4. 代码修改的地方 5.结果展示 1. 前言 之前一直在忙着写文档&#xff0c;之前一直做分类&#xff0c;检测和分割&#xff0c;现在看到跟踪算法&#xff0c;花了几天时间找代码调试&#xff0c;看了看&#xff0c;展示效果比单纯的检…

DevOps系列文章之 GitlabCICD自动化部署SpringBoot项目

一、概述 本文主要记录如何通过Gitlab CI/CD自动部署SpringBoot项目jar包。 二、前期准备 准备三台 CentOS7服务器&#xff0c;分别部署以下服务&#xff1a; 序号系统IP服务1CentOS7192.168.56.10Gitlab2CentOS7192.168.56.11Runner &#xff08;安装Docker&#xff09;3Cen…

Seata简介

1、简介 Seata是一个开源的分布式事务解决方案&#xff0c;用于解决分布式系统中的事务一致性问题。它提供了高性能和高可靠性的分布式事务支持&#xff0c;可以在微服务架构中保证数据的一致性和可靠性。 Seata的核心概念包括三个组件&#xff1a;事务协调器&#xff08;Tra…

如何正确下载tomcat???

亲爱的小伙伴&#xff0c;千万别再去找下网站下载啦&#xff0c;这样詪容易携带病毒。 我们去官方网址下载。 Apache Tomcat - Welcome! 最后下载解压即可。。。