java设计模式学习之【责任链模式】

news2024/10/2 12:35:35

文章目录

  • 引言
  • 责任链模式简介
    • 定义与用途
    • 实现方式
  • 使用场景
  • 优势与劣势
  • 在Spring框架中的应用
  • 日志示例
  • 代码地址

引言

在现实生活中,常常会遇到这样的场景:一个请求或命令需要经过多个层级的处理。例如,一个行政审批流程可能需要通过多个部门的审核。在软件开发中,我们可以使用责任链模式来模拟这种层级处理流程。责任链模式允许我们将请求的发送者和接收者解耦,将多个处理对象连成一条链,依次处理请求。

责任链模式简介

定义与用途

责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许将请求沿着处理链传递,直到一个对象处理它为止。每个处理对象都包含逻辑来处理请求或将请求传递给链上的下一个对象。

实现方式

实现责任链模式通常包括以下几个关键组件:

  • 处理器接口(Handler): 定义了处理请求的接口。
  • 具体处理器(Concrete Handlers): 实现处理器接口,并执行具体的处理逻辑或将请求传递给链上的下一个处理器。
  • 客户端(Client): 发起请求,并将请求传递给链上的第一个处理器。

使用场景

责任链模式适用于以下场景:

  • 当多个对象可以处理一个请求,但具体由哪个对象处理在运行时才确定时。
  • 当想在不明确指定接收者的情况下,向多个对象中的一个提交请求时。
  • 当处理请求的一组对象应被动态指定时。

例如:

  1. 审批流程:不同级别的审批请求通过不同级别的管理层进行处理。
  2. 事件处理系统:例如 GUI 中的事件传递,事件可以由多个对象处理。
  3. 日志记录:根据消息的严重性级别,决定将其发送到不同的输出目标。

优势与劣势

  • 优势
    降低耦合度:请求的发送者和接收者之间没有直接的耦合关系。
    增强灵活性:可以动态地改变链内的成员或调整其顺序。
    易于扩展:可以通过增加新的处理器来扩展。
  • 劣势
    不能保证请求一定会被接收:所有处理者都可能不处理请求,使其未被处理。
    对链中请求处理者的排列顺序和数量敏感。

在Spring框架中的应用

在Spring框架中,责任链模式通常用于处理一系列的处理步骤或中间件。这种模式在Spring的多个组件中得到应用,最典型的是在Spring Security和Spring MVC中。

1. Spring Security中的责任链应用

在Spring Security中,责任链模式体现在过滤器链(Filter Chain)中。
每个请求都会通过一系列的安全过滤器,每个过滤器执行不同的安全检查和任务。这些过滤器包括:

Authentication Filter:负责用户认证。
Authorization Filter:负责检查用户是否有权限访问特定资源。
Exception Translation Filter:负责处理在安全认证过程中抛出的异常。
每个过滤器处理请求后决定是否将请求传递给链中的下一个过滤器,或者是终止请求并返回响应。这正是责任链模式的核心特征。

2. Spring MVC中的责任链应用

在Spring MVC中,拦截器(Interceptors)也是一种责任链模式的体现。
拦截器用于在处理请求前后执行各种任务,比如日志记录、权限检查、事务处理等。你可以定义多个拦截器,并将它们链接在一起,形成一个拦截器链。
每个拦截器决定是否:

在Controller处理请求之前执行某些操作。
在Controller处理完请求后执行某些操作。

3. Spring中的Filter Chain

Spring的另一个责任链应用是Spring Web中的Filter Chain。在Spring Web中,你可以定义多个过滤器来处理Web请求。
每个过滤器执行完任务后,可以决定是否将请求传递给链中的下一个过滤器。
这些过滤器可以处理跨站请求伪造(CSRF)保护、CORS、编码问题等。

日志示例

在这里插入图片描述
步骤 1:创建抽象日志类
首先定义了一个 AbstractLogger 抽象类,作为日志处理者的基类。

public abstract class AbstractLogger {
   public static int INFO = 1;
   public static int DEBUG = 2;
   public static int ERROR = 3;

   protected int level;

   // 链中的下一个责任元素
   protected AbstractLogger nextLogger;

   public void setNextLogger(AbstractLogger nextLogger){
      this.nextLogger = nextLogger;
   }

   public void logMessage(int level, String message){
      if(this.level <= level){
         write(message);
      }
      if(nextLogger !=null){
         nextLogger.logMessage(level, message);
      }
   }

   abstract protected void write(String message);
	
}

这个类定义了日志级别和处理请求的方法。如果此处理者能处理该级别的日志,它将输出日志;否则,它将请求转发给链中的下一个处理者。

步骤 2:创建具体的日志处理类
创建了具体的日志处理者类,扩展了 AbstractLogger。

public class ConsoleLogger extends AbstractLogger {

   public ConsoleLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {		
      System.out.println("标准控制台::Logger: " + message);
   }
}

public class ErrorLogger extends AbstractLogger {

   public ErrorLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {		
      System.out.println("错误控制台::Logger: " + message);
   }
}

public class FileLogger extends AbstractLogger {

   public FileLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {		
      System.out.println("文件::Logger: " + message);
   }
}

每个具体的处理者类负责处理特定级别的日志消息。

步骤 3:创建不同类型的日志处理者并形成链
定义了一个客户端类 ChainPatternDemo 来创建日志处理者链并使用该链处理消息。

public class ChainPatternDemo {
	
   private static AbstractLogger getChainOfLoggers(){

      AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
      AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
      AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);

      // 形成责任链
      errorLogger.setNextLogger(fileLogger);
      fileLogger.setNextLogger(consoleLogger);

      return errorLogger;	
   }

   public static void main(String[] args) {
      AbstractLogger loggerChain = getChainOfLoggers();

      loggerChain.logMessage(AbstractLogger.INFO, 
         "这是一条信息级别的消息。");

      loggerChain.logMessage(AbstractLogger.DEBUG, 
         "这是一条调试级别的消息。");

      loggerChain.logMessage(AbstractLogger.ERROR, 
         "这是一条错误级别的消息。");
   }
}

在这里插入图片描述

在这个客户端中,我们创建了不同级别的日志处理者并连接它们形成一条链。然后,我们发送不同级别的消息给责任链,可以看到消息被相应级别的处理者处理。

这个示例演示了责任链模式在处理具有不同处理级别的请求中的效力。通过改变链的结构或成员,可以灵活地改变请求处理的方式。

代码地址

23种设计模式相关代码后续会逐步提交到github上,方便学习,欢迎指点:
代码地址
https://github.com/RuofeiSun/lf-23Pattern

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

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

相关文章

H5 cryptojs加密算法

1. cryptojs是什么&#xff1f; 有时候项目涉及到的敏感数据比较多&#xff0c;为了信息安全&#xff0c;我们常常需要对一些数据进行接口加密处理&#xff0c;如编码、将明文转化为暗文、加密比对、AES BASE64 算法加密等。 接下来我们就分别说一下 CryptoJS 常用的一些方法。…

爬虫是什么?起什么作用?

【爬虫】 如果把互联网比作一张大的蜘蛛网&#xff0c;数据便是放于蜘蛛网的各个节点&#xff0c;而爬虫就是一只小蜘蛛&#xff0c;沿着网络抓取自己得猎物&#xff08;数据&#xff09;。这种解释可能更容易理解&#xff0c;官网的&#xff0c;就是下面这个。 爬虫是一种自动…

jvm对象探究

hostpot虚拟机对象探究 jvm虚拟机创建对象的流程 ava虚拟机&#xff08;JVM&#xff09;创建对象的过程包括以下步骤&#xff1a; 类加载&#xff1a; 首先&#xff0c;JVM会检查对象的类是否已经被加载。如果该类还没有被加载&#xff0c;JVM会通过类加载器加载该类的字节码…

TCP服务器的演变过程:多进程实现一对多的TCP服务器

使用多进程实现一对多的TCP服务器 一、前言二、新增使用的fork()函数三、实现步骤四、完整代码五、TCP客户端5.1、自己实现一个TCP客户端5.2、Windows下可以使用NetAssist的网络助手工具 小结 一、前言 手把手教你从0开始编写TCP服务器程序&#xff0c;体验开局一块砖&#xf…

【C++】unordered_set/unordered_multiset/unordered_map/unordered_multimap

我们下面来学习C的另外两个容器&#xff1a;unordered_set和unordered_map 目录 一、unordered系列关联式容器 二、unordered_map 2.1 unordered_map的介绍 2.2 unordered_map的接口说明 2.2.1 unordered_map的构造 2.2.2 unordered_map的容量 2.2.3 unordered_map的迭…

室内设计品牌网站搭建的作用是什么

随着人们生活质量日益提升&#xff0c;对其自身的居住环境也有了较高要求&#xff0c;每个人审美不一样&#xff0c;无论自己居住的房屋还是公司办公/商场等场景都需要设计不同的内容&#xff0c;还有各种设施的摆放及类别等都有讲究&#xff0c;尤其对公司及商场等环境&#x…

动态规划算法练习题

45. 跳跃游戏 II 中等 2K 相关企业 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j &…

EasyExcel 导出文件的格式化

阿里开源的这个库&#xff0c;让 Excel 导出不再复杂&#xff08;既要能写&#xff0c;还要写的好看&#xff09; 之前聊了 EasyExcel 的内容导出&#xff0c;本文主要说一下导出文件的格式化&#xff0c;格式化包括工作表/单元格样式和内容格式化。毕竟&#xff0c;有时候还是…

【leetcode21】合并两个有序链表Java代码讲解

12.22 21. 合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入&#xff1a;l1 [], l2…

Appium Server 启动失败常见原因及解决办法

Error: listen EADDRINUSE: address already in use 0.0.0.0:4723 如下图&#xff1a; 错误原因&#xff1a;Appium 默认的4723端口被占用 解决办法&#xff1a; 出现该提示&#xff0c;有可能是 Appium Server 已启动&#xff0c;关闭已经启动的 Appium Server 即可。472…

音视频类App广告变现如何破局,最大化广告变现收益,让应用增收?

音视频App已然成为了我们日常获取、发布和交换信息的重要方式&#xff0c;在音视频行业不断的拓展中&#xff0c;用户的渗透率提升。 据数据显示&#xff0c;我国网络视听用户的规模已达9亿人次&#xff0c;网民使用率也突破了90%。庞大的市场规模和用户需求吸引了大批开发者和…

SRE - 监控建设

监控⽂档 随着信息技术的迅速发展及其在商业和工业环境中的广泛应用,系统的可靠性成为了组织的生存之本。Site Reliability Engineering(SRE)作为一种实践的结合体,广泛地用于确保和提升软件系统的可靠性。其中,它的一个重要组成部分是制定和监控服务的关键性能指标(Ser…

行为型设计模式(五):访问者模式 观察者模式

访问者模式 Visitor 1、什么是访问者模式 访问者模式允许定义一些不改变数据结构的前提下的操作。通过这种方式&#xff0c;可以在不修改元素类的情况下定义新的操作。访问者模式常用于对复杂对象结构进行操作&#xff0c;而又不希望在这些对象上破坏封装性。 2、为什么使用…

摄影企业网站搭建的作用是什么

几乎每个成年人都有一部手机&#xff0c;在互联网信息时代&#xff0c;手机的作用不言而喻&#xff0c;拍照/摄像成为了不少人经常会做的事&#xff0c;拍一张美美的照片发到社交圈赢得赞声&#xff0c;或是为以后留下回忆或发给自己在意的人&#xff0c;但这只限于生活记叙类图…

收集足够苹果的最小花园周长(LeetCode日记)

LeetCode-1954-收集足够苹果的最小花园周长 题目信息: 给你一个用无限二维网格表示的花园&#xff0c;每一个 整数坐标处都有一棵苹果树。整数坐标 ( i , j ) (i, j) (i,j) 处的苹果树有 ∣ i ∣ ∣ j ∣ |i| |j| ∣i∣∣j∣ 个苹果。 你将会买下正中心坐标是 ( 0 , 0 )…

免费使用谷歌Gemini模型学习LLM编程

虽然谷歌的Gemini大语言模型爆出很大的乌龙&#xff0c;但这不影响我们使用Gemini Pro来学习LLM编程。 目前Bard还没有全部切换为Gemini Pro模型&#xff0c;但是作为程序员&#xff0c;已经不需要等待&#xff0c;可以直接调用Gemini Pro的接口了。谷歌这次开发者优先的做法值…

12.23C语言 指针

& 地址运算符&#xff0c;用于取地址 /*注释内容*/ //注释一行 *的意思&#xff1a;1.算术运算符 2.用于指针声明int *ptr;表示这个变量是一个指针3.数组元素访问&#xff1a;在数组名后面使用 * 可以表示数组的起始地址。例如&#xff1a; int arr[5] {1, 2, 3, 4, 5…

WEB 3D技术 three.js 通过lil-gui 控制x y z轴数值 操作分组 设置布尔值控制 颜色材质控制

上文 WEB 3D技术 three.js 通过lil-gui管理公共事件中 我们用 lil-gui 处理了一下基础事件和按钮的管理 那么 本文 我们来具体说说它能做的其他事 我们先将基础代码改成这样 import ./style.css import * as THREE from "three"; //引入lil-gui import { GUI } fro…

题解:CF1914E-Game with Marbles

题解&#xff1a;CF1914E-Game with Marbles 事先说明一下&#xff0c;本题解不讲解简单数据范围的算法&#xff0c;因为复杂数据范围的就很简单。 这道题的大体意思是这样的&#xff1a;小A有颜色为i(i1~n)的小球a[i]个&#xff0c;小B有颜色为i(i1~n)的小球b[i]个。现在他们…

有关List的线程安全、高效读取:不变模式下的CopyOnWriteArrayList类、数据共享通道:BlockingQueue

有关List的线程安全 队列、链表之类的数据结构也是极常用的&#xff0c;几乎所有的应用程序都会与之相关。在java中&#xff0c; ArrayList和Vector都使用数组作为其内部实现。两者最大的不同在与Vector是线程安全的。 而ArrayList不是。此外LinkedList使用链表的数据结构实现…