设计模式学习(十一)责任链模式

news2024/9/24 19:23:37

目录

    • 一、定义
      • 1.1 主要成员
      • 1.2 优点
      • 1.3 缺点
    • 二、使用场景
      • 2.1 Spring Security 中的应用
    • 三、代码示例
      • 3.1 实现思路
      • 3.2 实体类
      • 3.3 抽象处理者
      • 3.4 具体处理者
        • 1)责任链容器
        • 2)校验-用户名
        • 3)校验-密码
        • 4)校验-手机号
      • 3.4 客户端(测试类)

一、定义

责任链模式: 是一种行为设计模式,它可以将请求从一个对象传递到另一个对象,知道找到能够处理该请求的对象为止。

在责任链模式中,每个对象代表一个处理请求的节点,并持有一个指向下一个节点的引用。当一个请求进入责任链时,第一个节点会尝试处理该请求,如果该节点无法处理请求,则将请求传递给下一个节点。这个过程会一直持续下去,直到找到一个能够处理请求的节点或者整个链结束。

1.1 主要成员

  • 抽象处理者(Abstract Handler): 定义了处理请求的接口,并持有一个指向下一个处理者的引用。
  • 具体处理者(Concrete Handler): 实现抽象处理者接口,具体处理请求的逻辑。如果它无法处理请求,可以将请求传递给下一个处理者。
  • 客户端(Client): 创建责任链并将请求发送到责任链的第一个节点。

1.2 优点

1)解耦发起者和处理者: 发起者不需要知道处理请求的具体者,只需要将请求发送给责任链的第一个节点即可,而具体的处理者由责任链自动决定。

2)提高灵活性和扩展性:可以随时添加、修改或删除处理者节点,以满足不同的需求和业务场景。

3)可以动态地改变处理顺序:可以根据具体情况来灵活地调整节点的顺序,以适应不同的处理逻辑。

1.3 缺点

1)性能问题:由于责任链模式需要依次传递请求给每个节点,可能会导致处理时间比较长,特别是当责任链中的节点数量很大时。此外,节点的处理顺序也可能影响性能,如果节点的处理逻辑和顺序设计不好,可能会导致性能瓶颈。

2)无法保证请求被处理:责任链模式中,请求被传递给责任链中的节点,直到找到能够处理请求的节点为止。如果整个责任链都无法处理请求,那么请求可能会被无视或丢失。

3)可能导致调试困难:由于责任链模式将请求传递给多个节点进行处理,当出现问题时,可能会难以追踪到具体是那个节点处理出了问题。

4)责任链的长度和复杂性:责任链模式中,整个链路可能包含很多节点,特别是在复杂的业务需求下, 责任链的长度和复杂性可能会变得很高。这会使责任链的创建、维护和理解变得困难。

因此,在应用责任链模式时,需要权衡其优点和缺点,根据具体需求和场景来决定是否使用责任链模式以及如何进行设计和使用。


二、使用场景

模板模式策略模式责任链模式 这三种模式具有相同的作用:复用和扩展。在日常开发中,主要用于替换复杂的 if-else 分支判断。

2.1 Spring Security 中的应用

例如:Spring Security 中的过滤器链可以看作是一种责任链模式的实现。

在 Spring Security 中,存在一个特殊的过滤器链,用于处理 Web 请求的安全认证和授权。这个过滤器链由一系列的过滤器组成,每个过滤器都扮演者特定的角色和功能,如身份验证、授权处理、会话管理等。

当一个 Web 请求进入 Spring Security 的过滤链式,请求会依次经过每个过滤器进行处理。每个过滤器会根据自己的功能进行处理,并决定是否将请求传递给下一个过滤器。 如果当前过滤器无法处理请求,可以将请求传递给下一个过滤器,直到找到能够处理该请求的过滤器为止。

在这个过程中,每个过滤器都持有一个指向下一个过滤器的引用,形成了一个链式结构。这种方式可以 有效地解耦和组织处理逻辑,使得责任链中的每个过滤器都只需要关注自己的功能,而不需要关注整个过滤器链的处理过程


三、代码示例

3.1 实现思路

需求:

  • 账号注册时进行校验,先后校验姓名、密码、手机号等。

使用责任链默认实现上述需求,可以消除很多 if-else 分支,增加功能的扩展性。
如果在责任链中增加一个校验,只需新建一个类即可,这个类就是责任链中的请求元素,可以选择性使用一个、多个或所有的请求对象。

责任链的具体实现方式有两种:

  • 链表式: 将下一个节点保存到当前节点的属性中,每次调用前通过 add() 设定下一个节点。
  • 数组式: 将所有节点保存到数组中,每次调用遍历数组。

我们这里以数组式为例,包结构如下:

在这里插入图片描述

3.2 实体类

UserInfo.java

import lombok.Data;

@Data
public class UserAccount {

    /**
     * 姓名
     */
    private String userName;

    /**
     * 密码
     */
    private String password;

    /**
     * 电话号码
     */
    private String phoneNumber;
}

3.3 抽象处理者

Verify.java

import com.demo.entity.UserInfo;

public interface Verify {

    /**
     * 验证过程
     *
     * @param userInfo  用户信息
     * @param chain     下一个验证节点
     */
    void doVerify(UserInfo userInfo, VerifyChain chain);

}

3.4 具体处理者

1)责任链容器

VerifyChain.java

import com.demo.entity.UserInfo;

import java.util.ArrayList;
import java.util.List;

public class VerifyChain implements Verify {

    /**
     * 验证节点集合
     */
    private List<Verify> verifyList = new ArrayList<>();

    /**
     * 验证节点索引
     */
    private ThreadLocal<Integer> index = ThreadLocal.withInitial(() -> 0);

    /**
     * 添加验证节点
     */
    public VerifyChain addVerify(Verify verify) {
        verifyList.add(verify);
        return this;
    }

    @Override
    public void doVerify(UserInfo userInfo, VerifyChain chain) {
        if (index.get() == verifyList.size()) {
            return;
        }
        Verify verify = verifyList.get(index.get());
        index.set(index.get() + 1);
        verify.doVerify(userInfo, chain);
    }
}
2)校验-用户名

UserNameVerify.java

import com.demo.entity.UserInfo;
import org.springframework.util.StringUtils;

public class UserNameVerify implements Verify {

    @Override
    public void doVerify(UserInfo userInfo, VerifyChain chain) {
        if (!StringUtils.hasText(userInfo.getUserName())) {
            System.out.println("用户名不能为空");
            return;
        }
        System.out.println("用户名验证通过");
        chain.doVerify(userInfo, chain);
    }
}
3)校验-密码

PasswordVerify.java

import com.demo.entity.UserInfo;
import org.springframework.util.StringUtils;

public class PasswordVerify implements Verify {

    @Override
    public void doVerify(UserInfo userInfo, VerifyChain chain) {
        if (!StringUtils.hasText(userInfo.getPassword())) {
            System.out.println("密码不能为空");
            return;
        }
        System.out.println("密码验证通过");
        chain.doVerify(userInfo, chain);
    }
}
4)校验-手机号

PhoneNumberVerifyChain.java

import com.demo.entity.UserInfo;
import org.springframework.util.StringUtils;

public class PhoneNumberVerify implements Verify {

    @Override
    public void doVerify(UserInfo userInfo, VerifyChain chain) {
        if (!StringUtils.hasText(userInfo.getPhoneNumber()) || userInfo.getPhoneNumber().length() != 11) {
            System.out.println("手机号码格式不正确");
            return;
        }
        System.out.println("手机号码验证通过");
        chain.doVerify(userInfo, chain);
    }
}

3.4 客户端(测试类)

import com.demo.chain.PasswordVerify;
import com.demo.chain.PhoneNumberVerify;
import com.demo.chain.UserNameVerify;
import com.demo.chain.VerifyChain;
import com.demo.entity.UserInfo;

public class MainTest {

    public static void main(String[] args) {
        UserInfo userInfo = new UserInfo("ACGkaka", "123456", "12345678901");
        VerifyChain verifyChain = new VerifyChain();
        // 校验顺序:用户名 -> 密码 -> 电话号码
        verifyChain.addVerify(new UserNameVerify())
                .addVerify(new PasswordVerify())
                .addVerify(new PhoneNumberVerify());
        verifyChain.doVerify(userInfo, verifyChain);
    }
}

执行结果:

在这里插入图片描述

整理完毕,完结撒花~ 🌻





参考地址:

1.责任链模式,https://zhuanlan.zhihu.com/p/509058039

2.【设计模式】责任链模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 ),https://blog.csdn.net/shulianghan/article/details/118188083

3.SpringBoot中filter的使用详解及原理,https://blog.csdn.net/u014627099/article/details/84565603

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

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

相关文章

windows平台FairMOT的实现

环境&#xff1a;python3.6pytorch1.1.0torchvision0.3.0cuda9.2vs2015 该项目需要装3个c库&#xff08;dcn_v2&#xff0c;apex&#xff0c;cython_bbox&#xff09;特别坑&#xff0c;各种环境不匹配&#xff0c;各种bug。本人c小白&#xff0c;但是一路摸索总算成功了。下面…

【Redis学习1】Redis持久化机制详解

Redis持久化机制详解 一、Redis为什么需要持久化机制 Redis一般用作缓存&#xff0c;其数据存储在内存中&#xff0c;当Redis宕机后&#xff0c;内存中的数据将会丢失。因此使用缓存的时候&#xff0c;我们经常需要对内存中的数据进行持久化也就是将内存中的数据写入到硬盘中…

vscode ssh linux C++ 程序调试

vscode调试c++程序相比vs2022要复杂很多,vs2022可以"一键运行调试",vscode则需要自己配置。 ​vscode调试程序时,会在当前工作目录产生.vscode 目录, 该目录有两个重要文件launch.json和tasks.json, 下面介绍两种调试方法: 手动调试和自动调试。 手动调试 不管…

k8s安全机制

安全机制 一、机制说明二、认证&#xff08;Authentication&#xff09;HTTP Token 认证HTTP Base 认证HTTPS 证书认证&#xff08;最严格&#xff09; 三、鉴权&#xff08;Authorization&#xff09;角色角色绑定主体&#xff08;subject&#xff09;Role and ClusterRoleRol…

实现一个简单的线性回归和多项式回归(2)

对于多项式回归&#xff0c;可以同样使用前面线性回归中定义的LinearRegression算子、训练函数train、均方误差函数mean_squared_error&#xff0c;生成数据集create_toy_data,这里就不多做赘述咯~ 拟合的函数为 def sin(x):y torch.sin(2 * math.pi * x)return y1.数据集的建…

3、在 CentOS 8 系统上安装 PostgreSQL 15.4

PostgreSQL&#xff0c;作为一款备受欢迎的开源关系数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;已经存在了三十多年的历史。它提供了SQL语言支持&#xff0c;用于管理数据库和执行CRUD操作&#xff08;创建、读取、更新、删除&#xff09;。 由于其卓越的健壮性…

Linux网络监控工具 - nethogs

nethogs 是一个基于命令行的网络监控工具&#xff0c;用于实时监视每个进程的网络流量。它可以显示每个进程使用的带宽、连接数和数据包数量等信息。 安装 在大多数Linux发行版中&#xff0c;您可以使用包管理器来安装 nethogs。例如&#xff0c;在Ubuntu/Debian上&#xff0c…

【Java 进阶篇】CSS语法格式详解

在前端开发中&#xff0c;CSS&#xff08;层叠样式表&#xff09;用于控制网页的样式和布局。了解CSS的语法格式是学习如何设计和美化网页的关键。本文将深入解释CSS的语法格式&#xff0c;包括选择器、属性和值等基本概念&#xff0c;同时提供示例代码以帮助初学者更好地理解。…

【单片机】18-红外线遥控

一、红外遥控背景知识 1.人机界面 &#xff08;1&#xff09;当面操作&#xff1a;按键&#xff0c;旋转/触摸按键&#xff0c;触摸屏 &#xff08;2&#xff09;遥控操作&#xff1a;红外遥控&#xff0c;433M/2.4G无线通信【穿墙能力强】&#xff0c;蓝牙-WIFI-Zigbee-LoRa等…

WPFdatagrid结合comboBox

在WPF的DataGrid中希望结合使用ComboBox下拉框&#xff0c;达到下拉选择绑定的效果&#xff0c;在实现的过程中&#xff0c;遇到了一些奇怪的问题&#xff0c;因此记录下来。 网上能够查询到的解决方案&#xff1a; 总共有三种ItemSource常见绑定实现方式&#xff1a; 1.ItemS…

【bug日记】spring项目使用配置类和测试类操作数据库

最近学校课程要求使用spring操作数据库&#xff0c;时间有点久了&#xff0c;操作都不太熟悉了&#xff0c;遇到了很多坑&#xff0c;特此记录一下。 导入依赖 <!-- Spring Framework --> <dependency><groupId>org.springframework</groupId><ar…

用Nginx搭建一个可用的静态资源Web服务器

sudo wget http://dlib.net/files/dlib-19.24.tar.bz2下载需要的文件。 sudo tar jxf dlib-19.24.tar.bz2进行解压。 sudo mkdir /nginx/dlib在nginx安装目录/nginx创建一个新的目录dlib。 配置文件里边的内容如下&#xff1a; worker_processes 1; events {worker_con…

如何批量获取1688商品详情数据接口,1688商品详情数据接口

批量获取1688商品详情数据接口的步骤如下&#xff1a; 获取API接口权限。编写API请求代码。应用爬取下来的数据。 1688商品详情数据接口步骤如下&#xff1a; 注册成为1688开放平台的开发者&#xff0c;并创建一个应用&#xff0c;获取到所需的App Key和App Secret等信息。使…

SpringBoot 如何使用 Prometheus 进行监控

在当今的软件开发世界中&#xff0c;监控是至关重要的一部分。它允许开发人员和运维团队实时跟踪应用程序的性能、可用性和健康状况。Spring Boot是一个流行的Java框架&#xff0c;用于构建微服务和Web应用程序&#xff0c;而Prometheus是一个开源的监控和警报工具。本文将介绍…

数据结构和算法——线性结构

文章目录 前言线性表顺序表链表合并有序链表反转链表 队列循环队列双端队列资源分配问题 栈共享栈表达式求值递归处理迷宫问题 串串的模式匹配BF算法KMP算法next数组的求解next数组的优化 前言 本文所有代码均在仓库中&#xff0c;这是一个完整的由纯C语言实现的可以存储任意类…

spark-08

学习视频&#xff1a; 黑马程序员Spark全套视频教程&#xff0c;4天spark3.2快速入门到精通&#xff0c;基于Python语言的spark教程_哔哩哔哩_bilibili

增强LLM:使用搜索引擎缓解大模型幻觉问题

论文题目&#xff1a;FRESHLLMS:REFRESHING LARGE LANGUAGE MODELS WITH SEARCH ENGINE AUGMENTATION 论文地址&#xff1a;https://arxiv.org/pdf/2310.03214.pdf 论文由Google、University of Massachusetts Amherst、OpenAI联合发布。 大部分大语言模型只会训练一次&#…

Spring Data Redis使用方式

1.导入Spring Data Redis的maven坐标 pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 2. 配置Redis数据源 2.1application.yml文件…

idea compile项目正常,启动项目的时候build失败,报“找不到符号”等问题

1、首先往上找&#xff0c;看能不能找到如下报错信息 You aren’t using a compiler supported by lombok, so lombok will not work and has been disabled. 2、这种问题属于lombok编译失败导致&#xff0c;可能原因是依赖jar包没有更新到最新版本 3、解决方案 1&#xff09…

C语言编程实现只有一个未知数的两个多项式合并的程序

背景&#xff1a; 直接看题目把&#xff01;就是C语言写两个多项式多项式合并 题目要求&#xff1a; 1. 题目&#xff1a; 编程实现只有一个未知数的两个多项式合并的程序。如&#xff1a; 3x^26x7 和 5x^2-2x9合并结果为8x^24x16。 2. 设计要求 &#xff08;1&#xff09…