《设计模式》责任链模式

news2024/9/25 15:28:15

《设计模式》责任链模式

定义

  • 责任链模式将链中每一个节点都看成一个对象,并且将这些节点对象连成一条链,请求会沿着这条链进行传递,直到有对象处理它为止,这使得多个对象都有机会接收请求,避免了请求发送者和接收者之间的耦合。
  • 属于行为型设计模式。

责任链模式的角色组成

  • Handler(抽象处理者):定义一个处理请求的抽象方法,并维护一个下一个处理节点对象的引用。
  • ConcreteHandler(具体处理者):实现了抽象处理请求方法,并在处理之前进行判断是否有相应的处理权限,有则处理,没有则将请求转发后继者处理。

责任链模式的 UML 类图
在这里插入图片描述

🎈情景案例:大家在公司上班难免遇到有事需要请假的情况,就我所在的公司来说,请假时间不超过3个工作日的,自己的直接领导(leader)可以直接审批,但是如果大于三个工作日不超过五个工作日的,则需要经理(CommonManager)进行审批了,而请假时间超过5个工作日的需要总经理(GeneralManager)进行审批了。其实,请假审批流程这样的场景就可以使用责任链模式模拟,提出的请假请求被一层层传递转发,直到最终的决策者。

抽象处理者 Manager

public abstract class Manager {
    protected String name;

    protected Manager superior;

    public Manager(String name) {
        this.name = name;
    }

    public void setSuperior(Manager superior) {
        this.superior = superior;
    }

    public abstract void requestAbsence(int days);
}

具体处理者 Leader

public class Leader extends Manager{

    public Leader(String name) {
        super(name);
    }

    @Override
    public void requestAbsence(int days) {
        if (days <= 3) {
            System.out.println(String.format("请假%s天被批准,审核人为%s!", days, name));
        } else {
            if (superior != null) {
                System.out.println(String.format("请假%s天被批准,审核人为%s,还需上级领导审核!", days, name));
                superior.requestAbsence(days);
            }
        }
    }
}

具体处理者 CommonManager

public class CommonManager extends Manager{

    public CommonManager(String name) {
        super(name);
    }

    @Override
    public void requestAbsence(int days) {
        if (days > 3 && days <= 5) {
            System.out.println(String.format("请假%s天被批准,审核人为%s!", days, name));
        } else {
            if (superior != null) {
                System.out.println(String.format("请假%s天被批准,审核人为%s,还需上级领导审核!", days, name));
                superior.requestAbsence(days);
            }
        }
    }
}

具体处理者 GeneralManager

public class GeneralManager extends Manager {

    public GeneralManager(String name) {
        super(name);
    }

    @Override
    public void requestAbsence(int days) {
        if (days > 5) {
            System.out.println(String.format("请假%s天被批准,审核人为%s!", days, name));
        }
    }
}

客户端 Client

public class Client {
    public static void main(String[] args) {
        Leader leader = new Leader("主管");
        CommonManager commonManager = new CommonManager("经理");
        GeneralManager generalManager = new GeneralManager("总经理");

        leader.setSuperior(commonManager);
        commonManager.setSuperior(generalManager);
      
        leader.requestAbsence(6);
    }
}

责任链模式的优点

  • 请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有的候选处理者的引用,这样的责任链可以简化对象的相互连接,降低耦合度
  • 链路结构灵活,可以通过改变链路结构动态地新增或删减责任
  • 易于扩展新的请求处理类(节点),符合开闭原则

责任链模式的缺点

  • 责任链太长或者处理时间过长,会影响整体性能。
  • 如果建链不当,可能会造成循环调用,将导致系统陷入死循环。
  • 一个请求也可能因职责链没有被正确配置而得不到处理

责任链模式的适用场景

  • 多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定。
  • 在不明确指定接收者的情况下,向多个对象中的一个提交请求。
  • 需要可以动态指定一组对象处理请求,客户端可以动态创建责任链来处理请求,还可以改变链中处理者之间的先后次序。

🎈责任链模式在JDK源码javax.servlet包中的应用

Servlet API 中提供了一个 Filter (过滤器)接口,通过过滤器技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截,一般常用于实现 URL 级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

Servlet API 中的 Filter 源码如下:

public interface Filter {
	// 过滤器第一次初始化时执行,初始化配置参数,在doFilter()方法之前被调用
    default void init(FilterConfig filterConfig) throws ServletException {
    }
	// 在客户端请求及服务器端回复时都将被自动调用,服务器每次在调用web资源之前,都会先调用一下该方法
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
	// 结束过滤器,doFilter()方法完成后被调用
    default void destroy() {
    }
}

Filter 相当于责任链模式中的抽象处理者 Handler,它是由 doFilter() 方法的最后一个参数 FilterChain 实现一条责任链的,其源码如下:

public interface FilterChain {

    /**
     * 调用链中的下一个过滤器,如果是调用链中最后一个过滤器,将调用链中最后一个资源
     * @param request 将沿着链请求
     * @param response 将沿着链回复           
     */
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
    
}

FilterChain 类中只定义了一个 doFilter() 方法上,J2EE 只定义了一个规范,具体处理逻辑是由使用者自己来实现,例如 Spring 框架中的实现 MockFilterChain 类:

public class MockFilterChain implements FilterChain {
    @Nullable
    private ServletRequest request;
    @Nullable
    private ServletResponse response;
    
    private final List<Filter> filters;
    
    @Nullable
    private Iterator<Filter> iterator;

    public MockFilterChain() {
        this.filters = Collections.emptyList();
    }

    public MockFilterChain(Servlet servlet) {
        this.filters = initFilterList(servlet);
    }

    public MockFilterChain(Servlet servlet, Filter... filters) {
        Assert.notNull(filters, "filters cannot be null");
        Assert.noNullElements(filters, "filters cannot contain null values");
        this.filters = initFilterList(servlet, filters);
    }

    private static List<Filter> initFilterList(Servlet servlet, Filter... filters) {
        Filter[] allFilters = (Filter[])ObjectUtils.addObjectToArray(filters, new MockFilterChain.ServletFilterProxy(servlet));
        return Arrays.asList(allFilters);
    }

    @Nullable
    public ServletRequest getRequest() {
        return this.request;
    }

    @Nullable
    public ServletResponse getResponse() {
        return this.response;
    }

    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        Assert.notNull(request, "Request must not be null");
        Assert.notNull(response, "Response must not be null");
        Assert.state(this.request == null, "This FilterChain has already been called!");
        if (this.iterator == null) {
            this.iterator = this.filters.iterator();
        }

        if (this.iterator.hasNext()) {
            Filter nextFilter = (Filter)this.iterator.next();
            nextFilter.doFilter(request, response, this);
        }

        this.request = request;
        this.response = response;
    }

    public void reset() {
        this.request = null;
        this.response = null;
        this.iterator = null;
    }

    private static final class ServletFilterProxy implements Filter {
        private final Servlet delegateServlet;

        private ServletFilterProxy(Servlet servlet) {
            Assert.notNull(servlet, "servlet cannot be null");
            this.delegateServlet = servlet;
        }

        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            this.delegateServlet.service(request, response);
        }

        public void init(FilterConfig filterConfig) throws ServletException {
        }

        public void destroy() {
        }

        public String toString() {
            return this.delegateServlet.toString();
        }
    }
}

MockFilterChain 类把链中的所有 Filter 都放到 List 中,然后在调用 doFilter() 方法时循环迭代List,也就是说 List 中的 Filter 会按顺序执行。

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

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

相关文章

warp框架教程3-path, method和自定义请求方法

path, method和自定义请求方法 path 是 warp 中的路由系统, 一个 web 框架的灵魂所在, 一个优美的路由系统可以给我们带来非常良好的使用体验, 而 warp 的路由体验本身就是非常 nice 的。在本文中将展示一个 RESTful 风格的 API 设计。下面先来学习一下 path 模块。 path 模块…

从小白到大神之路之学习运维第56天--------shell脚本实例应用2.0之有趣的知识

第三阶段基础 时 间&#xff1a;2023年7月10日 参加人&#xff1a;全班人员 内 容&#xff1a; shell实例 目录 shell脚本应用&#xff1a; 一、if判断 1、if判断的类型 1&#xff09;单分支 2&#xff09;双分支 3&#xff09;多分支 2、单分支if判断 1&#x…

Could not increase number of max_open_files to more than 5000 (request: 65535)

修改MySQL 打开文件数量限制 修改内核限制 ulimit -n //查看系统限制 修改 /etc/security/limits.conf 添加 soft nofile 65530hard nofile 65535 mysql> SHOW VARIABLES LIKE open_files_limit; 通过 MySQL 命令行检查新限制。您可以使用以下查询&#xff0c;确保设置了新…

vue3使用less入门使用案例(webStrom)

文章目录 简介安装less基础代码效果 less进阶代码效果 简介 less&#xff1a;css预处理语言 安装 npm i less3.0.4 -Dless打包解释器 npm i less-loader5.0.0 -Dless基础代码 <template><div class"a"></div> </template><style lang…

Java Web Servlet (1)23.7.7

Servlet 1&#xff0c; Servlet 1.1 简介 Servlet是JavaWeb最为核心的内容&#xff0c;它是Java提供的一门动态web资源开发技术。 使用Servlet就可以实现&#xff0c;根据不同的登录用户在页面上动态显示不同内容。 Servlet是JavaEE规范之一&#xff0c;其实就是一个接口&a…

【日常记录】CentOS7.5 安装tomcat放行8080端口后无法访问

环境 操作系统版本信息&#xff1a; [rootlocalhost conf]# cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core)JAVA版本信息 [rootlocalhost conf]# java -version openjdk version "1.8.0_372" OpenJDK Runtime Environment (build 1.8.0_372-b07) …

闲人闲谈PS之四十四——供应链透明化

惯例闲话&#xff1a; 近期工作和天气一样&#xff0c;如火如荼&#xff0c;前面1个月&#xff0c;拼全力搞了一个新功能&#xff0c;把闲人折腾的够呛&#xff0c;现在回头看看这个过程&#xff0c;倒也回味无穷&#xff0c;如何利用信息化工具&#xff0c;搞点数字化的活&am…

ORA-39168: Object path STATISTICS was not found

上周某客户因异常断电导致lun&#xff0c;某个测试环境无法启动&#xff0c;客户只提供了一个1周前.dmp文件&#xff0c;需要在新环境中导入恢复&#xff0c;解决表空间和临时表空间问题后&#xff0c;导入报错如下 Import: Release 11.2.0.4.0 - Production on Thu Jul 6 07:…

基础篇--Cortex-M系列介绍

视频教程 体系结构&#xff08;ARM架构版本&#xff09;与其对应的ARM处理器内核 Cortex-M系列介绍 ARM公司 ARM公司&#xff1a;只做内核设计和IP授权&#xff0c;不参与芯片设计 ARM架构为什么能风靡全球&#xff1f; Cortex内核分类及特征 Cortex-M3/4/7介绍

几个有趣的Python库,建议收藏~

随着每个 Python 版本的发布&#xff0c;都会添加新模块&#xff0c;并引入新的更好的做事方式&#xff0c;虽然我们都习惯了使用好的旧 Python 库和某些做事方式&#xff0c;但现在也时候升级并利用新的和改进的模块及其特性了。 Pathlib pathlib 绝对是 Python 标准库中最近…

python 第十二章 面向对象

系列文章目录 第一章 初识python 第二章 变量 第三章 基础语句 第四章 字符串str 第五章 列表list [] 第六章 元组tuple ( ) 第七章 字典dict {} 第八章 集合set {} 第九章 常用操作 第十章 函数 第十一章 文件操作 文章目录 系列文章目录12.1面向对象实现方法定义类经典类&am…

《Redis 核心技术与实战》课程学习笔记(五)

数据同步&#xff1a;主从库如何实现数据一致&#xff1f; 那我们总说的 Redis 具有高可靠性&#xff0c;又是什么意思呢&#xff1f; 其实&#xff0c;这里有两层含义&#xff1a;一是数据尽量少丢失&#xff0c;二是服务尽量少中断。AOF 和 RDB 保证了前者&#xff0c;而对于…

【NLP,Huggingface,Colab】使用 Trainer 训练模型,并保存模型参数

【NLP&#xff0c;Huggingface&#xff0c;Colab】使用 Trainer 训练模型&#xff0c;并保存模型参数 前置知识上代码 前置知识 Colab 的使用Huggingface 官网和一些基础API 上代码 首先&#xff0c;建议保存代码到 VSCode&#xff0c;这样双击关键类&#xff0c;F12可以进入…

一个HTTP的流程

1&#xff0c;键入一个URL后浏览器将URL进行解析 2,浏览器解析URL后&#xff0c;需要查询服务器域名对应的IP地址。 流程如下&#xff1a;查询缓存&#xff0d; >客户端发送DNS请求-> 根DNS&#xff0c;根DNS根据 .COM-> 顶级域名服务器&#xff0c;根据baidu->权…

【Linux后端服务器开发】管道设计

目录 一、管道通信 二、匿名管道 1. 匿名管道通信 2. 匿名管道设计 三、命名管道 comm.hpp client.cc serve.cc 一、管道通信 进程通信 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程 资源共享&#xff1a;多个进程之间共享同样的资源 通知事件&…

Matlab建模实战学习——(规划问题)

1.Matlab函数linprog&#xff08;&#xff09;的使用 1.1 Matlab基本描述 1.2 函数linprog&#xff08;&#xff09; 三种表达形式 [x,fval] linprog(c,A,b)[x,fval] linprog(c,A,b,Acq,bcq)[x,fval]linprog(c,A,b,Aeq,beq,lb,ub) 其中 x返回的是决策向量的取值&#xf…

K8S应用服务安全(最小特权 策略方案 资源限制 调用限制 沙箱)

应用服务安全 1 应用服务安全1.1 最小特权1.1.1 基础知识1.1.2 安全上下文1.1.3 资源实践1.1.4 特权基础1.1.5 特权实践1.1.5 PSP实践 1.2 策略方案1.2.1 OPA简介1.2.2 特权策略1.2.3 策略进阶 1.3 资源限制1.3.1 AppArmor简介1.3.2 基础知识1.3.2 应用实践 1.4 调用限制1.4.1 …

Git装机基础配置

Git在开发中可谓是必用的工具软件了&#xff0c;下面介绍下装机必须的几个配置 配置用户身份 git config –global user.name “Your Name” git config –global user.email “emailexample.com”// 例如&#xff1a; git config –global user.name “xiaowag” git config …

openpnp - 负压传感器DP102的设置

文章目录 openpnp - 负压传感器DP102的设置概述笔记上电后的画面查看模式进入设置模式输出1检测模式输出2检测模式输出1和输出2的常态(常开/常闭状态)设置动作响应时间输出动作颜色的设置压力单位的设置回到正常的运行模式输出1下限的设置输出1上限的设置输出2下限的设置输出2上…

有趣的命令——————用shell脚本实现三角形(直角三角形、等腰三角形)

直角三角形 vim zhijiao.sh 输入以下内容&#xff1a;#!/bin/bashread -p "几层的三角形:" nfor ((i1;i<$n;i)) dofor ((jn;j>i;j--))doecho -n " "donefor ((j1;j<i;j))doecho -n "*"doneecho done例&#xff1a; 测试&#xff1a; 等…