《解锁高效流程设计:深度剖析责任链模式与实战应用》

news2024/10/2 22:30:22

《解锁高效流程设计:深度剖析责任链模式与实战应用》

责任链模式 是一种行为设计模式,它允许多个对象来处理请求,而不预先指定具体的处理者。多个处理对象被连接成一条链,沿着这条链传递请求,直到某个处理对象决定处理这个请求为止。责任链模式通过将请求的发送者与接收者解耦,来提高系统的灵活性。

UML 类图:责任链模式

在这里插入图片描述

角色说明

  1. Handler(抽象处理者):定义处理请求的接口,通常包含一个 setNext() 方法用于设置下一个处理者,以及 handleRequest() 方法处理请求或将其传递给下一个处理者。
  2. ConcreteHandler(具体处理者):具体处理请求的类。如果它能处理请求则直接处理,否则将请求传递给下一个处理者。
  3. Request(请求对象):请求对象,包含一些必要的信息,具体的处理者会根据请求中的信息决定是否处理。

实战场景:重构权限审批流程

问题描述

在企业内部的权限审批系统中,不同的人员可以审批不同级别的权限请求。例如:

  • 普通员工只能审批简单权限(如普通数据访问权限)。
  • 部门主管可以审批更高级别的权限请求(如敏感数据访问权限)。
  • 管理层可以审批最高级别的权限请求(如系统管理权限)。

当一个权限请求发出后,它会沿着审批链传递,直到找到能处理该请求的人员。使用责任链模式,可以让请求沿着处理链传递,并且避免将处理逻辑硬编码在某个具体对象中。

代码实现:权限审批流程的责任链构建

Step 1: 定义请求类

首先,我们定义一个 Request 类来封装权限请求。

// 请求类:封装权限请求
public class Request {
    private String requestType;
    private int permissionLevel;

    public Request(String requestType, int permissionLevel) {
        this.requestType = requestType;
        this.permissionLevel = permissionLevel;
    }

    public String getRequestType() {
        return requestType;
    }

    public int getPermissionLevel() {
        return permissionLevel;
    }
}

Step 2: 定义抽象处理者

定义抽象的 Handler 接口,所有具体的处理者都实现该接口。该接口定义了 setNext() 方法用于设置下一个处理者,handleRequest() 方法用于处理请求或将请求传递给下一个处理者。

// 抽象处理者接口
public abstract class Handler {
    protected Handler nextHandler;

    // 设置下一个处理者
    public void setNext(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    // 抽象方法:处理请求
    public abstract void handleRequest(Request request);
}

Step 3: 实现具体的处理者

根据实际的权限审批流程,我们创建三个具体的处理者:普通员工、部门主管、管理层。

普通员工处理者

普通员工只能处理权限级别为 1 的请求。

// 具体处理者:普通员工
public class EmployeeHandler extends Handler {

    @Override
    public void handleRequest(Request request) {
        if (request.getPermissionLevel() == 1) {
            System.out.println("Employee approves request for: " + request.getRequestType());
        } else {
            if (nextHandler != null) {
                nextHandler.handleRequest(request);
            }
        }
    }
}

部门主管处理者

部门主管可以处理权限级别为 2 的请求。

// 具体处理者:部门主管
public class SupervisorHandler extends Handler {

    @Override
    public void handleRequest(Request request) {
        if (request.getPermissionLevel() == 2) {
            System.out.println("Supervisor approves request for: " + request.getRequestType());
        } else {
            if (nextHandler != null) {
                nextHandler.handleRequest(request);
            }
        }
    }
}

管理层处理者

管理层可以处理权限级别为 3 的请求。

// 具体处理者:管理层
public class ManagerHandler extends Handler {

    @Override
    public void handleRequest(Request request) {
        if (request.getPermissionLevel() == 3) {
            System.out.println("Manager approves request for: " + request.getRequestType());
        } else {
            System.out.println("Request not approved. No handler for permission level: " + request.getPermissionLevel());
        }
    }
}

Step 4: 创建处理链并测试

我们可以创建处理链,并将权限请求传递给链中的第一个处理者。

public class ChainOfResponsibilityDemo {

    public static void main(String[] args) {
        // 创建处理者
        Handler employee = new EmployeeHandler();
        Handler supervisor = new SupervisorHandler();
        Handler manager = new ManagerHandler();

        // 设置责任链
        employee.setNext(supervisor);
        supervisor.setNext(manager);

        // 创建权限请求
        Request request1 = new Request("Access to ordinary data", 1);
        Request request2 = new Request("Access to sensitive data", 2);
        Request request3 = new Request("System admin access", 3);
        Request request4 = new Request("Access to confidential data", 4);  // 没有处理者能处理

        // 开始处理请求
        employee.handleRequest(request1);  // 应由普通员工处理
        employee.handleRequest(request2);  // 应由部门主管处理
        employee.handleRequest(request3);  // 应由管理层处理
        employee.handleRequest(request4);  // 没有处理者能处理
    }
}

输出结果

Employee approves request for: Access to ordinary data
Supervisor approves request for: Access to sensitive data
Manager approves request for: System admin access
Request not approved. No handler for permission level: 4

责任链模式解决的问题

1.解耦请求发送者和接收者

  • 请求的发送者不知道请求的最终处理者是谁,处理者可以动态地决定是否处理请求或将请求传递给下一个处理者。

2.灵活的责任分配

  • 通过责任链的方式,可以动态地增加或减少处理者。系统可以根据实际需要自由调整处理链,增强了系统的灵活性。

3.避免代码的条件分支嵌套

  • 通过将责任分布在不同的处理者中,避免了大量的 if-elseswitch-case 语句,代码变得更加清晰且易于维护。

责任链模式的应用场景

  1. 审批流程
    • 比如企业的请假审批、费用报销审批流程。不同的审批人员有不同的权限,依次审批请求。
  2. 日志处理
    • 日志系统可以通过责任链模式,将日志消息依次传递给不同的处理者(如文件日志处理器、数据库日志处理器等)。
  3. 事件处理系统
    • GUI 事件处理,比如用户点击按钮时,会沿着事件链将事件分发给不同的事件处理器,直到事件被处理

SpringMVC 源码中的责任链模式剖析

责任链模式 在 SpringMVC 中被广泛使用,特别是在处理 HTTP 请求的过程中。SpringMVC 通过一系列的拦截器链过滤器链处理器适配器链等来解耦请求的处理方式,这些链条中的各个元素会根据自身职责处理请求或将其传递给下一个元素,这种设计非常符合责任链模式的思想。

接下来我们将剖析 SpringMVC 中责任链模式的几个典型应用场景,包括:

  1. DispatcherServlet 的处理链
  2. HandlerInterceptor 拦截器链
  3. HandlerExceptionResolver 处理器链

1. DispatcherServlet 的处理链

背景:

DispatcherServlet 是 SpringMVC 的核心,它作为前端控制器,负责接收 HTTP 请求并将其分发给合适的处理器进行处理。DispatcherServlet 并不是直接处理请求的,它依赖一系列的处理器链,包括处理器映射(HandlerMapping)处理器适配器(HandlerAdapter) 等。这些组件的设计符合责任链模式的思想。

工作流程:
  1. DispatcherServlet 接收请求后,会依次调用配置的多个 HandlerMapping 来找到合适的处理器。
  2. 找到处理器后,依次调用配置的多个 HandlerAdapter 来处理该处理器。
  3. 若请求处理失败,DispatcherServlet 会依次调用配置的 HandlerExceptionResolver 来处理异常。

通过这种链式调用,SpringMVC 将处理器映射、处理器适配和异常处理等职责分散到多个组件中,使得请求的处理过程高度灵活。

关键代码剖析:
  • 处理器映射链DispatcherServlet 中调用 getHandler() 方法时,会遍历所有配置的 HandlerMapping,并找到合适的处理器。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

剖析

  • handlerMappings 是一个 HandlerMapping 列表,SpringMVC 允许配置多个 HandlerMapping,每个 HandlerMapping 都可以处理不同的请求映射。
  • getHandler() 会遍历所有 HandlerMapping,如果一个 HandlerMapping 可以处理当前请求,它就会返回对应的处理器,否则请求会被传递给下一个 HandlerMapping

这里的处理器映射链就是一个典型的责任链模式,每个 HandlerMapping 相当于一个链条上的处理者。

处理器适配器链: 找到处理器后,DispatcherServlet 会使用 HandlerAdapter 来执行该处理器的业务逻辑。SpringMVC 支持多种类型的处理器(如控制器、异步处理等),因此需要通过不同的 HandlerAdapter 来处理不同类型的处理器。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }
    throw new ServletException("No adapter for handler [" + handler + "]");
}

剖析

  • handlerAdapters 是一个 HandlerAdapter 列表,每个 HandlerAdapter 都可以处理不同类型的处理器。
  • getHandlerAdapter() 会依次遍历 HandlerAdapter 列表,找到可以处理当前处理器的适配器,并使用它来执行处理器。
  • 这种处理器适配器的机制也是典型的责任链模式应用,不同的 HandlerAdapter 负责处理不同类型的请求处理器。

总结

优点:

  1. 降低耦合度:请求的发送者与接收者解耦,发送者不需要知道请求是由哪个具体的处理者处理的。
  2. 动态组合:可以灵活地增加或修改处理者链中的处理者,处理链的结构可以在运行时动态修改。
  3. 符合开闭原则:新的处理者可以很容易地加入到责任链中,不需要修改现有的处理者类。

缺点:

  1. 请求没有保证被处理:如果处理链中的所有处理者都没有处理请求,可能会导致请求未被处理的情况。
  2. 性能问题:如果责任链过长,可能会导致处理请求的效率降低,因为每个请求可能要经过多个处理者。

责任链模式 提供了一种灵活的处理请求的方式,通过将多个处理者连接成一条链,使得请求可以沿着链传递,直到被处理。通过使用责任链模式,我们可以解耦请求的发送者和处理者,并且可以动态增加或修改处理者,增强了系统的扩展性。

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

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

相关文章

【前端 25】

Ant Design框架使用教程:构建高效美观的React应用 引言 Ant Design 是一套企业级的 UI 设计语言和 React 组件库,主要用于开发和服务于企业级后台产品。它基于 React,并遵循 Ant Design 设计规范,提供了大量高质量、易用的 React…

Spring Boot集成Milvus快速入门demo

1.什么是Milvus? Milvus 是一种高性能、高扩展性的向量数据库,可在从笔记本电脑到大型分布式系统等各种环境中高效运行。它既可以开源软件的形式提供,也可以云服务的形式提供。 Milvus 是 LF AI & Data Foundation 下的一个开源项目&…

计算机的错误计算(一百零四)

摘要 计算机的错误计算(二十七)引入了错数概念。本节给出更为严格的证明。 本节主要讨论表达式计算结果中错误有效数字的数量,简称之为错数。因为0不含有有效数字,因此,除非特别说明,否则,本节…

【Go】-Websocket的使用

目录 为什么需要websocket 使用场景 在线教育 视频弹幕 Web端即时通信方式 什么是web端即时通讯技术? 轮询 长轮询 长连接 SSE websocket 通信方式总结 Websocket介绍 协议升级 连接确认 数据帧 socket和websocket 常见状态码 gorilla/websocket实…

10-pg内核之锁管理器(五)行锁

概念 数据库采用MVCC方式进行并发控制,读写并不会互相阻塞,但是写之间仍然存在冲突。如果还是采用常规锁那样加锁,则会耗费大量共享内存,进而影响性能。所以行锁通过元组级常规锁和xmax结合的方式实现。一般先通过xmax进行可见性…

Unity 新导航寻路演示(2)

对于静态场景来说,只需要3步 1.为场景Ground添加网格表面组件并烘焙 2.为player添加导航代理 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI;public class PlayerMove : MonoBehaviour {private NavMes…

2D动画转3D角色!无需建模- comfyUI工作流一键生成3d效果图!

如何将2d角色转化成3d角色? 不需要建模,通过一个2d转3d的工作流可以直接将你的2d图片转化成3d效果图。 而且操作特别简单,只需要3个步骤,这篇内容我们来说下这个工作路的使用 工作流特点 任意2D图片转换成3D风格 基于sd1.5模型…

ftdi_sio驱动学习笔记 3 - 端口操作

目录 1. ftdi_port_probe 1.1 私有数据结构ftdi_private 1.2 特殊probe处理 1.3 确定FTDI设备类型 1.4 确定最大数据包大小 1.5 设置读取延迟时间 1.6 初始化GPIO 1.6.1 使能GPIO 1.6.2 添加到系统 1.6.2.1 设置GPIO控制器的基本信息 1.6.2.2 设置GPIO控制器的元信息…

Apache Iceberg 与 Spark整合-使用教程(Iceberg 官方文档解析)

官方文档链接(Spark整合Iceberg) 1.Getting Started Spark 目前是进行 Iceberg 操作最丰富的计算引擎。官方建议从 Spark 开始,以理解 Iceberg 的概念和功能。 The latest version of Iceberg is 1.6.1.(2024年9月24日11:45:55&…

如何在云端使用 Browserless 进行网页抓取?

云浏览器是什么? 云浏览器是一种基于云的组合,它将网页浏览器应用程序与一个虚拟化的容器相结合,实现了远程浏览器隔离的概念。开发人员可以使用流行的工具(如 Playwright 和​ Puppeteer​)来自动化网页浏览器&#…

repo 查看指定日期内,哪些仓库有修改,具体的修改详情

文章目录 想看指定时间段内仓库中修改了哪些具体的文件,是谁修改的,commit的备注信息等详情只想看某段时间内有更改的仓库的修改详情,其他没有修改的仓库不显示。 想看指定时间段内仓库中修改了哪些具体的文件,是谁修改的&#xf…

VSCode#include头文件时找不到头文件:我的解决方法

0.前言 1.在学习了Linux之后,我平常大部分都使用本地的XShell或者VSCode连接远程云服务器写代码,CentOS的包管理器为我省去了不少繁琐的事情,今天使用vscode打开本地目录想写点代码发现#include头文件后,下方出现了波浪线&#…

SparkSQL-初识

一、概览 Spark SQL and DataFrames - Spark 3.5.2 Documentation 我们先看下官网的描述: SparkSQL是用于结构化数据处理的Spark模块,与基本的Spark RDD API不同。Spark SQL提供的接口为Spark提供了更多关于正在执行的数据和计算结构的信息。在内部&a…

C++中vector类的使用

目录 1.vector类常用接口说明 1.1默认成员函数 1.1.1构造函数(constructor) 1.1.2 赋值运算符重载(operator()) 2. vector对象的访问及遍历操作(Iterators and Element access) 3.vector类对象的容量操作(Capacity) 4. vector类对象的修改及相关操作(Modifiers and Stri…

【Java数据结构】 ---对象的比较

乐观学习,乐观生活,才能不断前进啊!!! 我的主页:optimistic_chen 我的专栏:c语言 ,Java 欢迎大家访问~ 创作不易,大佬们点赞鼓励下吧~ 前言 上图中,线性表、堆…

[Redis][主从复制][上]详细讲解

目录 0.前言1.配置1.建立复制2.断开复制3.安全性4.只读5.传输延迟 2.拓扑1.一主一从结构2.一主多从结构2.树形主从结构 0.前言 说明:该章节相关操作不需要记忆,理解流程和原理即可,用的时候能自主查到即可主从复制? 分布式系统中…

PyTorch自定义学习率调度器实现指南

在深度学习训练过程中,学习率调度器扮演着至关重要的角色。这主要是因为在训练的不同阶段,模型的学习动态会发生显著变化。 在训练初期,损失函数通常呈现剧烈波动,梯度值较大且不稳定。此阶段的主要目标是在优化空间中快速接近某…

ResNet残差网络:深度学习的里程碑

引言 在深度学习领域,卷积神经网络(CNN)的发展一直推动着图像识别、目标检测等任务的进步。然而,随着网络层数的增加,传统的CNN面临着梯度消失和梯度爆炸等难题,限制了深层网络的训练效果。为了克服这些挑…

oracle direct path read处理过程

文章目录 缘起处理过程1.AWR Report 分析2.调查direct path read发生的table3.获取sql text4.解释sql并输出执行计划: 结论:补充direct path read等待事件说明 缘起 记录direct path read处理过程 处理过程 1.AWR Report 分析 问题发生时间段awr如下…

FortiGate OSPF动态路由协议配置

1.目的 本文档针对 FortiGate 的 OSPF 动态路由协议说明。OSPF 路由协议是一种 典型的链路状态(Link-state)的路由协议,一般用于同一个路由域内。在这里,路由 域是指一个自治系统,即 AS,它是指一组通过统一的路由政策或路由协议互相交 换路由信息的网络。在这个 AS 中,所有的 …