【设计模式】第十五章:责任链模式详解及应用案例

news2024/12/24 0:26:32

系列文章

【设计模式】七大设计原则
【设计模式】第一章:单例模式
【设计模式】第二章:工厂模式
【设计模式】第三章:建造者模式
【设计模式】第四章:原型模式
【设计模式】第五章:适配器模式
【设计模式】第六章:装饰器模式
【设计模式】第七章:代理模式
【设计模式】第八章:桥接模式
【设计模式】第九章:外观模式 / 门面模式
【设计模式】第十章:组合模式
【设计模式】第十一章:享元模式
【设计模式】第十二章:观察者模式
【设计模式】第十三章:模板方法模式
【设计模式】第十四章:策略模式
【设计模式】第十五章:责任链模式
【设计模式】第十六章:迭代器模式
【设计模式】第十七章:状态模式
【设计模式】第十八章:备忘录模式
【设计模式】第十九章:访问者模式
【设计模式】第二十章:解释器模式
【设计模式】第二十一章:命令模式
【设计模式】第二十二章:中介者模式


文章目录

  • 系列文章
  • 一、定义
  • 二、角色分类
  • 三、实现方式
    • UML图
    • 具体实现
  • 四、应用场景
  • 五、优缺点
    • 优点
    • 缺点


一、定义

摘自百度百科: 在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。


二、角色分类

抽象处理者(Handler)

定义了处理请求的接口或者抽象类,并提供了处理请求的的方法和设置下一个处理者的方法

具体处理者(Concrete Handler)

它是抽象处理者的子类,可以处理用户请求,在具体处理者类中实现了抽象处理者中定义的抽象请求处理方法,在处理请求之前需要进行判断,看是否有相应的处理权限,如果可以处理请求就处理它,否则将请求转发给后继者;在具体处理者中可以访问链中下一个对象,以便请求的转发

客户角色(Client)

具体调用方法的角色


三、实现方式

UML图

Image.png

具体实现

假如我们想用责任链模式实现一个请假流程,可以这么来实现

抽象处理者(Handler)

@Data
public abstract class Handler {
  // 处理者姓名
  protected String processorName;
  // 下一个处理者
  protected Handler nextHandler

  public Handler (String processorName) {
    this.processorName = processorName;
  }

  /**
   * 处理请假抽象方法
   * @param name 请假人姓名
   * @param numOfDays 请假天数
   */
  public abstract boolean process(String name, int numOfDays);
}

具体处理者(Concrete Handler)

/**
 * 组长
 */
@Slf4j
public class TeamLeaderHandler extends Handler {
  public TeamLeaderHandler(String processorName) {
    this.processorName = processorName;
  }

  @Override
  public boolean process(String name, int numOfDays) {
    // 创建随机数,值大于3则为通过,否则为不通过
    boolean res = (new Random().nextInt(10)) > 3;
    String result = res ? "通过" : "驳回";
    log.info("组长<{}>审批<{}>的请假申请,请假天数为:<{}>天,审批结果为:<{}>", processorName, name, numOfDays, result);

    if(Boolean.FALSE.equals(res)) {
      // 审批驳回
      return false;
    } else if (numOfDays < 3){
      // 请假通过且请假天数小于3天时直接通过
      return true;
    }
    // 若审批通过且请假天数大于等于3天时提交给下一个审批人
    return nextHandler.process(name, numOfDays);
  }
}

/**
 * 部门经理
 */
@Slf4j
public class DepartmentManagerHandler extends Handler {
  public DepartmentManagerHandler(String processorName) {
    super(name);
  }

  @Override
  public boolean process(String name, int numOfDays) {
    // 创建一个随机数 当随机数大于3时为通过,否则为不通过
    boolean res = (new Random().nextInt(10)) > 3;
    String result = result ? "通过" : "驳回";
    log.info("部门经理<{}>审批<{}>的请假申请,请假天数为:<{}>天,审批结果为:<{}>", processorName, name, numOfDays, result);

    if(Boolean.FALSE.equals(res)) {
      // 审批驳回
      return false;
    } else if (numOfDays <7) {
      // 审批通过且审批天数小于7天时直接通过
      return true;
    }
    // 批准天数大于等于7天时提交给下一个审批人
    return nextHandler.process(name, numOfDays);
  }

  /**
   * CEO
   */
  @Slf4j
  public class CEOHandler extends Handler {
    public CEOHandler(String processorName) {
      super(name);
    }

    public boolean process(String name, int numOfDays) {
      // 创建一个随机数,大于3则为通过,否则为驳回
      boolean res = (new Random().nextInt(10)) > 3;
      String result = res ? "通过" : "驳回";
      log.info("CEO<{}>审批<{}>的请假申请,请假天数为:<{}>天,审批结果为:<{}>", processorName, name, numOfDays, result);

      if(Boolean.FALSE.equals(res)) {
        //驳回
        return false;
      }
      return true;
    } 
  } 
}

客户角色(Client)

public class Client {
  public static void main(String[] args) {
    Handler zhangsan = new TeamLeaderHandler("张三");
    Handler lisi = new DepartmentManagerHandler("李四");
    Handler wangwu = new CEOHandler("王五");

    // 创建责任链
    zhangsan.setNextHandler(lisi);
    lisi.setNextHandler(wangwu);

    // 发起请假申请
    boolean res1 = zhangsan.process("小A", 2);
    System.out.println("最终结果:" + res);

    boolean res1 = zhangsan.process("小B", 5);
    System.out.println("最终结果:" + res);
    
    boolean res1 = zhangsan.process("小C", 10);
    System.out.println("最终结果:" + res);
  }
}

运行结果

组长<张三>审批<小A>的请假申请,请假天数为:<2>天,审批结果为:<通过>
最终结果:true

部门经理<李四> 审批 <小B> 的请假申请,请假天数: <5>天 ,审批结果:<不通过> 
最终结果:false

组长<张三> 审批 <小C> 的请假申请,请假天数: <10> 天,审批结果:<通过> 
部门经理<李四> 审批 <小C> 的请假申请,请假天数: <10> 天,审批结果:<通过> 
CEO<王五> 审批 <小C> 的请假申请,请假天数: <10> 天,审批结果:<通过> 
最终结果:true

四、应用场景

以下部分内容摘自菜鸟教程

意图: 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

主要解决: 职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

何时使用: 在处理消息的时候以过滤很多道。

如何解决: 拦截的类都实现统一接口。

关键代码: Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。

应用实例:

  1. 红楼梦中的"击鼓传花"。
  2. JS 中的事件冒泡。
  3. JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。

使用场景:

  1. 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
  2. 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  3. 可动态指定一组对象处理请求。

注意事项: 在 JAVA WEB 中遇到很多应用。


五、优缺点

优点

  1. 降低耦合度。它将请求的发送者和接收者解耦。
  2. 简化了对象。使得对象不需要知道链的结构。
  3. 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
  4. 增加新的请求处理类很方便。

缺点

  1. 不能保证请求一定被接收。
  2. 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
  3. 可能不容易观察运行时的特征,有碍于除错。

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

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

相关文章

银河麒麟服务器v10 sp1 nginx开机自动启动

接上一篇&#xff1a;银河麒麟服务器v10 sp1 安装 nginx_csdn_aspnet的博客-CSDN博客 设置开机自启动 定义服务启动文件内容&#xff1a; [Unit] Descriptionnginx - high performance web server Afternetwork.target remote-fs.target nss-lookup.target [Service] Ty…

menuconfig selected by 怎么处理

比方说我想取消掉flex&#xff0c;但是被强制生成了&#xff1a; 输入搜索命令查了一下&#xff1a; 搜一下selected by [y] 中的 linux_pam: 取消掉 这样就不用编flex了。

【HarmonyOS】元服务启动命令漫谈

在日常开发中&#xff0c;我们可以通过DevEco Studio&#xff0c;直接Run我们的元服务工程&#xff0c;在测试机上拉起我们开发的元服务页面。但是我们自己打包HarmonyOS元服务hap在手机上安装后是没有桌面图标的。虽然我们可以在设置的服务管理中找到我们安装的元服务&#xf…

Go语言操作MySql数据库

go-sql-driver/mysql库是Go语言官方推荐的MySQL驱动库&#xff0c;可以很方便地实现对MySQL数据库的连接和操作。本文记录以下使用go-sql-driver/mysql数据库驱动来操作mysql数据库。 目录 1.安装驱动程序 2.导入驱动包 3.操作数据库 3.1 获取mysql版本 3.2 创建表 3.3 …

详解JAVA Socket

目录 1.概述 2.使用 3.使用场景 3.1.web server中的网络通信 3.2.长连接 3.3.性能问题 1.概述 什么是网络通信&#xff1a; 就像打电话一样&#xff0c;两点间要通信&#xff0c;两点间就必须有连接&#xff0c;为了实现任意两个节点之间的通信&#xff0c;我们就必须采…

用户与组管理介绍

文章目录 一、服务器系统版本介绍二、用户管理1. 用户概述2. 内置账户3. 配置文件4. 用户管理命令 三、组管理1. 组概述2. 内置组&#xff08;系统自带的组&#xff09;3. 组管理命令 一、服务器系统版本介绍 Windows服务器系统&#xff1a;win2000、win2003、win2008、win2012…

Spring 与 Servlet-2

学习笔记&#xff08;加油呀&#xff09;&#xff1a; Spring的通知类型 Spring 通知类型按切面功能调用的不同时刻&#xff0c;可以分为提供了 5 种 Advice 类型 1、前置通知 Before advice&#xff1a;在某连接点之前执行的通知&#xff0c;但这个通知不能阻止连接点之前的…

SAP ABAP ALV FIELDCAT 字段设置详细说明

一、SAP ABAP ALV FIELDCAT 字段设置的位置&#xff1a; 二、SAP ABAP ALV FIELDCAT 字段设置的详细说明&#xff1a; 字段属性描述使用目的CFIELDNAME参照Currency单位的字段名根据单位显示相应值CHECKBOX设置成复选框字段输出选项COL POS字段的输出顺序字段输出选项COLDDICT…

Android Studio实现内容丰富的安卓视频管理平台

如需源码可以添加q-------3290510686&#xff0c;也有演示视频演示具体功能&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动。 项目编号081 1.开发环境 android stuido 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.本地视频 3.视频播放 4.收藏功能 5.网路视频…

R语言绘图丨论文中最常用箱线图绘制教程,自动进行显著性检验和误差线标注

多组比较式箱线图 在科研论文绘图中&#xff0c;对于多组数据进行比较一般采用箱线图的方法&#xff0c;今天分享一下这个经典数据可视化方法&#xff0c;从零开始绘制一张带误差棒并自动计算显著性比较结果的箱线图。 前言&#xff1a;箱线图有什么优势&#xff1f; 数据分布…

【AcWing算法基础课】第三章 搜索与图论

文章目录 前言课前温习一、深度优先搜索&#xff08;DFS&#xff09;1、排列数字1.1题目描述1.2思路分析1.3代码实现 2、 n-皇后问题1.4题目描述1.5思路分析1.6代码实现 二、宽度优先搜索&#xff08;BFS&#xff09;1、走迷宫2.1题目描述2.2思路分析2.3代码实现 三、树与图的存…

2023.07.05 ARM day6

实验1 1.在键盘输入一个字符&#xff0c;串口工具进行显示 2.例如&#xff1a;在在键盘输入一个字符a,串口工具进行显示b 实验2 1.在键盘输入一个字符串&#xff0c;串口工具进行显示 2.例如&#xff1a;在在键盘输入一个字符串“huyue”,串口工具进行显示“huyue” inclu…

MySQL数据库管理与开发

什么是MySQL 数据库&#xff1f; M典MQLB公司开发的一个关系型数据库管理系统。通过它司以有效地组织和管理存储在数据库中的数据。MySQL 数据库可以称得上日前运行速度最快的SQL语言数据库。 MySQL 的优势 MySQL数据库是一款自由软件&#xff0c;任何人都可以从MySQL的官方…

保姆级 雅特力AT32 MCU 从SRAM启动KEIL工程配置步骤(STM/GD/APM通用)

好记性不如烂笔头&#xff0c;既然不够聪明&#xff0c;就乖乖的做笔记&#xff0c;温故而知新。 本文档用于本人对知识点的梳理和记录。 一、前言 开发工具&#xff1a;KEIL 开发板&#xff1a;AT32F415 AT-START-F415 软件工程&#xff1a;雅特力BSP flash_wirte_read 点击…

阿里开业项目chat2DB-人工智能SQL分析介绍

1. chat2DB简介 1-1. 简介 ​ chat2DB是一款有开源免费的多数据库客户端工具&#xff0c;支持windows、mac本地安装&#xff0c;也支持服务器端部署&#xff0c;web网页访问。和传统的数据库客户端软件Navicat、DBeaver 相比Chat2DB集成了AIGC的能力&#xff0c;能够将自然语…

RabbitMQ系列(9)--RabbitMQ预取值及利用预取值实现不公平分发

概念&#xff1a;RabbitMQ的默认分发消息机制是轮询分发&#xff0c;但在消费者之间处理任务速度不同时&#xff0c;这种分发消息机制会导致任务的处理效率低下&#xff0c;处理任务速度快的消费者很大一部分的时间处于空闲状态&#xff0c;速度慢的消费者则一直在干活&#xf…

SpringBoot教学资料6-SpringBoot登录注册功能实现(带简单前端)

项目样式&#xff1a; SQL: CREATE TABLE t_user (id int(11) NOT NULL AUTO_INCREMENT,username varchar(32) NOT NULL,password varchar(32) NOT NULL,PRIMARY KEY (id),UNIQUE KEY username (username) ) ENGINEInnoDB AUTO_INCREMENT5 DEFAULT CHARSETutf8项目结构&#xf…

数据结构(排序)

文章目录 一、排序的概念二、插入排序1. 基本思想2. 直接插入排序3. 希尔排序(缩小增量排序) 三、选择排序1. 基本思想2. 直接选择排序3. 堆排序 四、交换排序1. 基本思想2. 冒泡排序3. 快速排序 五、归并排序六、其他排序6.1 计数排序6.2 基数排序6.3 桶排序 一、排序的概念 …

记一次 .NET 某医院预约平台 非托管泄露分析

一&#xff1a;背景 1. 讲故事 前几天有位朋友找到我&#xff0c;说他的程序有内存泄露&#xff0c;让我帮忙排查一下&#xff0c;截图如下&#xff1a; 说实话看到 32bit&#xff0c; 1.5G 这些关键词之后&#xff0c;职业敏感告诉我&#xff0c;他这个可能是虚拟地址紧张所…

Docker快速部署Hadoop环境

Docker安装部署Hadoop环境&#xff0c;通过三个容器来模拟三个节点&#xff0c;最后只保留Master节点实现搭建。 安装环境 Ubuntu 22.04.1 LTS 和Docker 23.0.1 安装过程 拉取镜像 docker pull registry.cn-hangzhou.aliyuncs.com/hadoop_test/hadoop_base在Docker中创建网…