【设计模式】Java 设计模式之责任链模式(Chain of Responsibility)

责任链模式(Chain of Responsibility)

一、概述

责任链模式是一种行为设计模式,它允许请求在对象链中传递。每个对象都有机会处理该请求,并且能将其传递给链中的下一个对象。这种模式为请求创建了一个处理对象的链,并沿着这条链传递该请求,直到有一个对象处理它为止。

二、模式结构

责任链模式主要包含以下几个角色:

  1. 抽象处理者(Handler)角色:定义了一个处理请求的接口,同时含有一个后继者的引用。

  2. 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以则处理,否则将该请求转发给它的后继者。

  3. 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求。

三、实现方式

通常,实现责任链模式时,我们会在每个处理者对象中维护一个指向下一个处理者的引用。当处理者无法处理请求时,它会将该请求传递给下一个处理者。

四、代码示例

下面是一个简单的责任链模式的代码示例:

// 抽象处理者
public abstract class Handler {protected Handler nextHandler;public void setNextHandler(Handler handler) {this.nextHandler = handler;}public abstract void handleRequest(String request);
}// 具体处理者A
public class ConcreteHandlerA extends Handler {@Overridepublic void handleRequest(String request) {if ("TypeA".equals(request)) {System.out.println("ConcreteHandlerA handles request: " + request);} else {if (nextHandler != null) {nextHandler.handleRequest(request);} else {System.out.println("No handler for request: " + request);}}}
}// 具体处理者B
public class ConcreteHandlerB extends Handler {@Overridepublic void handleRequest(String request) {if ("TypeB".equals(request)) {System.out.println("ConcreteHandlerB handles request: " + request);} else {if (nextHandler != null) {nextHandler.handleRequest(request);} else {System.out.println("No handler for request: " + request);}}}
}// 客户类
public class Client {public static void main(String[] args) {Handler handlerA = new ConcreteHandlerA();Handler handlerB = new ConcreteHandlerB();handlerA.setNextHandler(handlerB);handlerA.handleRequest("TypeA"); // 输出: ConcreteHandlerA handles request: TypeAhandlerA.handleRequest("TypeB"); // 输出: ConcreteHandlerB handles request: TypeBhandlerA.handleRequest("TypeC"); // 输出: No handler for request: TypeC}
}
五、优缺点分析

优点

  1. 降低了对象之间的耦合度。该模式使得一个对象无须知道到底哪一个对象会处理其请求以及链的结构。
  2. 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
  3. 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。

缺点

  1. 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  2. 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
六、常见应用场景

责任链模式常见于以下场景:

  • 多个对象可以处理同一请求,但具体由哪个对象处理该请求在运行时确定。
  • 当你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  • 当系统需要动态地决定请求的处理者时。
七、实际应用案例解读

假设在一个日志系统中,不同类型的日志需要不同的处理器来处理,如错误日志、警告日志、信息日志等。我们可以使用责任链模式来构建一个日志处理链,每个处理器负责处理特定类型的日志,如果无法处理则传递给下一个处理器。

例如,ErrorHandler 处理错误日志,WarningHandler 处理警告日志,InfoHandler 处理信息日志。当收到一条日志时,先传递给 ErrorHandler,如果它不能处理(比如这条日志不是错误类型),则传递给 WarningHandler,如果还是不能处理,最后传递给 InfoHandler

代码示例:

// 日志处理器接口
public interface LogHandler {boolean handle(Log log);
}// 错误日志处理器
public class ErrorHandler implements LogHandler {@Overridepublic boolean handle(Log log) {if (log.getLevel().equals(LogLevel.ERROR)) {System.out.println("Error log handled: " + log.getMessage());return true; // 已处理}return false; // 未处理,传递给下一个处理器}
}// 警告日志处理器
public class WarningHandler implements LogHandler {private LogHandler nextHandler;public WarningHandler(LogHandler nextHandler) {this.nextHandler = nextHandler;}@Overridepublic boolean handle(Log log) {if (log.getLevel().equals(LogLevel.WARNING)) {System.out.println("Warning log handled: " + log.getMessage());return true;}if (nextHandler != null) {return nextHandler.handle(log);}return false;}
}// 信息日志处理器
public class InfoHandler implements LogHandler {@Overridepublic boolean handle(Log log) {if (log.getLevel().equals(LogLevel.INFO)) {System.out.println("Info log handled: " + log.getMessage());return true;}return false;}
}// 日志类
public class Log {private LogLevel level;private String message;// 省略构造器和其他方法
}// 日志级别枚举
public enum LogLevel {ERROR, WARNING, INFO
}// 客户类
public class LoggingSystem {public static void main(String[] args) {LogHandler errorHandler = new ErrorHandler();LogHandler warningHandler = new WarningHandler(new InfoHandler());// 组装责任链warningHandler.handle(new Log(LogLevel.ERROR, "This is an error log")); // Error log handledwarningHandler.handle(new Log(LogLevel.WARNING, "This is a warning log")); // Warning log handledwarningHandler.handle(new Log(LogLevel.INFO, "This is an info log")); // Info log handledwarningHandler.handle(new Log(LogLevel.DEBUG, "This is a debug log")); // 无处理器处理,因为没有对应的Debug处理器}
}

在这个例子中,ErrorHandlerWarningHandlerInfoHandler 各自负责处理特定类型的日志。WarningHandler 持有对 InfoHandler 的引用,以便在无法处理警告日志时传递给 InfoHandler。如果 InfoHandler 也不能处理,那么该日志就不会被进一步处理。

八、总结

责任链模式提供了一种将多个请求处理器组织成链的方式,使得请求能够沿着链传递直到被处理。它降低了请求发送者和多个请求处理者之间的耦合度,提高了系统的灵活性和可扩展性。然而,使用责任链模式时需要注意,过长的责任链可能会影响系统性能,且需要确保每个请求都能被正确处理,避免请求丢失。在实际应用中,应根据具体场景和需求来选择合适的实现方式。
九、责任链模式的适用场景

责任链模式在许多场景中都非常有用,特别是在以下情况中:

  1. 多个处理器按特定顺序处理请求:当系统中有多个组件需要按特定顺序处理某个请求时,责任链模式可以确保请求按照正确的顺序传递到每个组件。

  2. 动态改变处理流程:如果处理请求的流程经常需要动态改变,责任链模式非常适用。通过添加、删除或重新排序处理器,可以轻松改变处理流程。

  3. 未知的处理者数量:当处理请求所需的处理器数量未知或可能变化时,责任链模式允许灵活地添加或删除处理器,而无需修改其他代码。

  4. 日志记录、异常处理或权限检查:这些通常涉及多个级别的处理,每个级别可能有自己的处理逻辑。责任链模式可以确保这些请求按正确的顺序和逻辑进行处理。

  5. GUI事件处理:在图形用户界面(GUI)中,用户交互事件(如点击、键盘输入等)通常需要经过多个处理阶段,如事件分发、处理、反馈等。责任链模式可以帮助组织这些处理阶段。

  6. 中间件或管道处理:在分布式系统或中间件中,请求可能需要经过多个服务或组件的处理。责任链模式可以确保请求按照正确的顺序和逻辑在这些服务或组件之间传递。

十、责任链模式的优缺点

优点

  1. 降低了耦合度:客户端不需要知道链的结构,也不需要指定请求的处理者,只需将请求发送到链的起始点即可。

  2. 增强了系统的可扩展性:可以很容易地添加新的请求处理者到链中,以满足开闭原则。

  3. 增强了给对象指派职责的灵活性:能够根据运行时的条件动态地改变处理者的顺序。

缺点

  1. 不能保证每个请求都被处理:由于请求是沿着链传递的,如果链中的某个处理者没有正确处理请求,或者链本身存在问题(如循环引用),那么请求可能不会被正确处理。

  2. 系统性能可能下降:由于请求需要在链中传递,可能会增加处理时间,特别是在链较长或处理者较多时。

  3. 调试可能较复杂:由于请求可能经过多个处理者,定位问题的来源可能比较困难。

十一、实际应用中的注意事项

  1. 避免无限循环:在设计责任链时,要确保没有循环引用,否则请求可能会在处理器之间无限循环。

  2. 提供明确的错误处理机制:当请求在链中无法被处理时,应提供明确的错误处理机制,如抛出异常或记录日志。

  3. 考虑性能优化:如果链较长或处理者较多,可以考虑使用缓存或其他优化技术来提高性能。

  4. 保持链的简洁性:尽量避免不必要的处理器,以保持链的简洁性和高效性。

  5. 文档化链的结构和行为:为了便于维护和调试,应详细记录链的结构、每个处理器的职责以及处理逻辑。

通过深入了解责任链模式的适用场景、优缺点以及实际应用中的注意事项,我们可以更好地利用该模式来设计和优化我们的软件系统。

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

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

相关文章

申请Github Education获取免费Copilot权限(2024.3.18实测成功)

起因:旧帐户Copilot权限被封 我已经离开Github Copilot就无法独自耐着性子写代码了(懒惰AI成瘾性),这两天Github Copilot不知道为什么在大规模封号,我不幸也被封号了(禁用掉了Github Copilot权限&#xff…

Python:柱状-折线图

写论文,需要画数据分析图: 用柱状图描述算法执行时间用折线图描述性能改进 示例代码: import numpy as np import matplotlib.pyplot as plt from matplotlib.pyplot import MultipleLocatorSecurity ["128", "192",…

RPC浅析,加密数据解析

个人总结 其实就是HOOK注入wbsocket 链接创建服务端和客户端进行通信,直接调用js代码中的加密方法 将结果通过浏览器客户端传入服务端。一种比较好实用的一种技术 https://blog.csdn.net/qq_36759224/article/details/123082574 (搬运记录下&#xff…

Linux第82步_“gpio子系统”下的使用KEY开关灯

使用新字符设备驱动的一般模板和“gpio子系统”,以及设备树,驱动KEY和LED。 1、在stm32mp157d-atk.dts文件中添加“gpio_led”和“key0”节点 打开虚拟机上“VSCode”,点击“文件”,点击“打开文件夹”,点击“zgq”&…

HTTPS 协议原理

目录 HTTPS VS HTTP HTTPS是什么 概念准备 常见的加密方式 对称加密 一个简单的对称加密例子 非对称加密 数据摘要&&数据指纹 数字签名 HTTPS 的工作过程探究 方案1-只使用对称加密 方案2-只使用非对称加密 方案3-双方都使用非对称加密 方案4-非对称加密…

Cell发表的单细胞整合方法:LIGER,很好用!

之前,我们已经介绍过其他的整合方法: harmony整合单细胞数据-去除批次效应 Seurat一键完成五种数据整合:harmony,CCA,RPCA,FastMNN,scVI,代码分享 今天来看看Cell发表的LIGER 介绍 LIGER(Li…

C++语言学习(二)—— C++语言的基本知识

目录 一、面向对象的三个核心概念 二、C语言中的I/O口 三、C语言中的数据类型​​​​​​​ 3.1 逻辑类型 3.2 引用类型 3.2.1 引用作为函数参数 3.2.2 引用作为函数返回值 3.2.3 引用作为类成员 3.3 类类型 四、 C语言中的内联函数 五、 函数重载 六、 带默认形参…

docker 容器挂掉,无法exec 进入bash 怎么修改容器里的文件

在使用tdengine 数据库时出现了 TDengine.Driver.TDengineError:“code:[0x334],error:Out of dnodes” 查找文档发现需要修改一个配置文件 。 /etc/taos/taos.cfg 中的 supportVnodes 参数 于是修改 保存。然后,运行出错。 03/21 06:56:27.986498 00000064 …

IDEA通过内网穿透实现固定公网地址远程SSH连接本地Linux服务器

文章目录 1. 检查Linux SSH服务2. 本地连接测试3. Linux 安装Cpolar4. 创建远程连接公网地址5. 公网远程连接测试6. 固定连接公网地址7. 固定地址连接测试 本文主要介绍如何在IDEA中设置远程连接服务器开发环境,并结合Cpolar内网穿透工具实现无公网远程连接&#xf…

[音视频学习笔记]六、自制音视频播放器Part1 -新版本ffmpeg,Qt +VS2022,都什么年代了还在写传统播放器?

前言 参考了雷神的自制播放器项目,100行代码实现最简单的基于FFMPEGSDL的视频播放器(SDL1.x) 不过老版本的代码参考意义不大了,我现在准备使用Qt VS2022 FFmpeg59重写这部分代码,具体的代码仓库如下: …

基于python+vue渔船出海及海货统计系统的设计与实现flask-django-php-nodejs

当今社会已经步入了科学技术进步和经济社会快速发展的新时期,国际信息和学术交流也不断加强,计算机技术对经济社会发展和人民生活改善的影响也日益突出,人类的生存和思考方式也产生了变化。传统渔船出海及海货统计采取了人工的管理方法&#…

抖音视频爬虫下载软件|可导出视频分享链接|视频批量采集工具

无水印视频关键词批量下载工具操作指南 我们自主开发了一款便捷的音视频批量下载工具,不仅支持单个视频链接提取,还可通过关键词搜索实现批量采集和选择性下载,让您轻松获取所需视频内容。 操作说明: 关键词批量采集视频 进入软件…

AI绘画可以稳定生成中文了:白嫖阿里云部署AnyText

长久以来,在AI绘画中书写文字一直是个难题。即使到了SDXL时代,我们也只能输出英文,而且还经常出现漏掉字母的情况。现在阿里达摩院搞出了一个解决方案,可以在Stable Diffusion生成的作品中稳定输出中、英、日、韩等多种文字&#…

python共享单车信息系统的设计与实现flask-django-php-nodejs

课题主要分为二大模块:即管理员模块和用户模块,主要功能包括:用户、区域、共享单车、单车租赁、租赁归还、报修信息、检修信息等; 语言:Python 框架:django/flask 软件版本:python3.7.7 数据库…

探索Code Llama 70B:Meta让AI辅助编程更易获得的举措

探索Code Llama 70B:Meta让AI辅助编程更易获取 在AI技术不断改变软件开发的今天,Meta推出了其最先进的开源基础模型,简化了软件开发流程。这个模型名为Code Llama 70B,旨在让AI辅助代码生成及其相关任务更容易被更广泛的受众获取…

【Unity】从0到1的横版2d制作笔记-DAY3

确定碰撞体积 选择rigidbody2d,创建player重力 创建player碰撞体积 创建瓦片地图碰撞体积 使平台变成一个整体 ​​​​​ 设置Body Type为Static(避免平台也因为重力影响下落) 回到Player,在Rigidbody2D中设置为冻结旋转 Player设…

python家政服务系统flask-django-php-nodejs

相比于以前的传统手工管理方式,智能化的管理方式可以大幅降低家政公司的运营人员成本,实现了家政服务的标准化、制度化、程序化的管理,有效地防止了家政服务的随意管理,提高了信息的处理速度和精确度,能够及时、准确地…

解决mini2440 LCD(型号:P43)驱动的背光失效问题

目录 概述 1 LCD(P43)背光问题 1 移植LCD驱动程序 1.1 编写驱动代码 1.2 编写LCD的配置参数 1.3 配置LCD驱动至内核 2 移植触摸功能程序 2.1 移植ADC驱动程序 2.1.1 编写ADC驱动代码 2.1.2 编写配置文件 2.2 移植P43的一线触摸程序 2.2.1 编写代码 2.2.2 编写配置链…

-bash: ./1.sh: /bin/bash^M: bad interpreter: No such file or directory解决方法

1、执行脚本 ./1.sh时报如下错误 -bash: ./1.sh: /bin/bash^M: bad interpreter: No such file or directory 2、在Windows编辑的脚本导入Linux系统中,执行报错问题 yum install -y dos2unix 3、或者本地安装 rpm -ivh /mnt/Packages/dos...... 4、然…

Flutter探索之旅:控制键盘可见性的神奇工具(flutter_keyboard_visibility)

随着移动应用的不断发展,用户体验的重要性愈发突显。而键盘的弹出和隐藏对于用户体验来说是至关重要的一环。在Flutter中,我们有幸拥有一个强大的工具——flutter_keyboard_visibility,它让我们能够轻松地监测键盘的可见性并做出相应的响应。…