设计模式——责任链模式

文章目录

    • 责任链模式的定义
    • 场景示例
    • 责任链模式实现方案
    • 责任链模式扩展
    • 责任链模式的优缺点
    • 责任链模式在框架源码中的应用

责任链模式的定义

责任链模式又称职责链模式,是一种行为型设计模式。官方描述:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

原文链接或文中示例代码下载,请关注公众号【Qin的学习营地】,回复:【责任链模式

场景示例

上述定义是对责任链模式这种思想的归纳总结,理解起来稍微抽象,下面结合一种场景逐步深入责任链模式。

企业采购

在企业中经常需要采购一些物品,小到一些廉价的生活用品,大到昂贵的精密器械,采购金额高低各异,不同采购金额需要由不同领导层次的管理人审批。比如金额在1万以下的只需部门负责人审批即可;金额超过1万且10万以下,部门负责人无权审批,需交由总经理审批;金额超过10万以上则只能层层上报到董事长,由董事长审批了,如下图。
在这里插入图片描述

简单的 if…else 实现方案

在上述企业采购中,采购金额不同分别交由不同层次的领导审批,这里最简单的实现方案就是使用 if…else 判断。代码如下:

// 采购审批处理类
public class PurchaseHandler {

    // 采购审批处理方法
    public String handleRequest(String purchaserName, Double purchaseAmount){
        if (purchaseAmount < 10000.0){ // 部门负责人审批
            return approvalByDepartmentLeader(purchaserName, purchaseAmount);
        } else if (purchaseAmount < 100000.0) { // 总经理审批
            return approvalByManager(purchaserName, purchaseAmount);
        } else { // 董事长审批
            return approvalByChairman(purchaserName, purchaseAmount);
        }
    }

    private String approvalByDepartmentLeader(String purchaserName, Double purchaseAmount){
        return (purchaserName + "申请采购金额:" + purchaseAmount + "元, 审批人:部门负责人");
    }

    private String approvalByManager(String purchaserName, Double purchaseAmount){
        return (purchaserName + "申请采购金额:" + purchaseAmount + "元, 审批人:总经理");
    }

    private String approvalByChairman(String purchaserName, Double purchaseAmount){
        return (purchaserName + "申请采购金额:" + purchaseAmount + "元, 审批人:董事长");
    }
}

if…else 实现方案存在的问题

分析 if…else 实现方案存在的问题:

  1. 代码结构不够清晰干净。
  2. 采购审批中的每个审批环节可能存在业务逻辑改动,比如部门负责人审核完之后,还是总经理签字,这就需要修改审批处理类和方法。
  3. 采购审批流程发生变化,当金额超过一定数额时,也不能完全由董事长审批,需要通过董事会投票决定,这就需要增加一个审批步骤,同样也需要修改源代码。

责任链模式实现方案

仔细分析上面的场景,客户端提出一个采购申请请求,对于客户端来说,他不需要关心是谁审批,后续审批流上可能是由部门负责人、总经理、董事长任一符合方进行审批。这样从部门负责人到总经理、再到董事长审批流程,可以看作是一条链,客户端提出的申请请求在这个链上传递,直到对应的责任人审批为止。

流程中可能在任意位置新增环节,每个环节可能会修改自己的处理逻辑,为了方便修改或复用每个环节;或者扩展新增环节,组合不同流程,可以将每个环节作为一个单独的对象,并且只实现单一的处理功能,这样细粒度划分功能,有利于降低修改的影响面和提高扩展性。

责任链模式概念

责任链模式就体现了上述细粒度抽取的思想,核心在于引入了一个抽象处理者。

责任链模式结构图:
在这里插入图片描述

责任链模式中的角色:

  • Handler(抽象处理者):一个处理请求的接口,一般设计为抽象类,其中定义一个处理请求的抽象方法,不同的具体处理者实现该方法;每个处理者可能需要将请求传递给下一个处理者,所以还可以定义一个处理者对象,作为后继者的引用,从而实现一条链。

  • ConcreteHandler(具体处理者):抽象处理者的具体实现子类,实现抽象处理类中的抽象方法,方法需要实现的两个功能:一是处理请求,处理具体处理者职责范围内的请求;二是转发请求,不在该具体处理者职责范围内的请求,就将请求转发给后继者处理。

  • Client(客户端):责任链客户端,组合处理者对象构造责任链,同时请求责任链处理请求对象。

    组合的顺序很重要,如果顺序不对,处理的结果可能就不是符合要求的。

    Client 不算责任链中的角色,外部使用责任链模式时自行创建。

责任链模式代码示例:

1、定义抽象处理者接口类,类中持有下一个处理者对象,同时实现请求处理方法:

// 定义责任链抽象接口类
public abstract class Handler {

    // 持有下一个处理者对象引用
    protected Handler nextHandler;

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

    // 处理采购申请请求
    public abstract String handleRequest(String purchaserName, Double purchaseAmount);
}

2、实现具体处理者类,实现处理请求的具体方法,方法内部逻辑:处理请求的具体逻辑,或者将请求对象传递给下一个处理者。这里实现三个具体处理者。

  • DepartmentLeaderHandler 部门负责人处理类

    // 部门负责人处理类
    public class DepartmentLeaderHandler extends Handler {
        @Override
        public String handleRequest(String purchaserName, Double purchaseAmount) {
            // 职责范围内处理审批请求
            if (purchaseAmount < 10000){
                return (purchaserName + "申请采购金额:" + purchaseAmount + "元, 审批人:部门负责人");
            } else {
                // 职责范围外处理,交由下一个处理者处理
                if (this.nextHandler != null){
                    return this.nextHandler.handleRequest(purchaserName, purchaseAmount);
                }
            }
            return null;
        }
    }
    
  • ManagerHandler 总经理处理类

    // 总经理处理类
    public class ManagerHandler extends Handler {
        @Override
        public String handleRequest(String purchaserName, Double purchaseAmount) {
            // 职责范围内处理审批请求
            if (purchaseAmount < 100000){
                return (purchaserName + "申请采购金额:" + purchaseAmount + "元, 审批人:总经理");
            } else {
                // 职责范围外处理,交由下一个处理者处理
                if (this.nextHandler != null){
                    return this.nextHandler.handleRequest(purchaserName, purchaseAmount);
                }
            }
            return null;
        }
    }
    
  • ChairmanHandler 董事长处理类

    // 董事长处理类
    public class ChairmanHandler extends Handler {
        @Override
        public String handleRequest(String purchaserName, Double purchaseAmount) {
            // 职责范围内处理审批请求
            if (purchaseAmount >= 100000){
                return (purchaserName + "申请采购金额:" + purchaseAmount + "元, 审批人:董事长");
            } else {
                // 职责范围外处理,交由下一个处理者处理
                if (this.nextHandler != null){
                    return this.nextHandler.handleRequest(purchaserName, purchaseAmount);
                }
            }
            return null;
        }
    }
    

3、客户端组合处理者构建责任链并调用

public class Client {

    public static void main(String[] args) {
        // 创建责任链上的每个处理者对象
        DepartmentLeaderHandler departmentLeaderHandler = new DepartmentLeaderHandler();
        ManagerHandler managerHandler = new ManagerHandler();
        ChairmanHandler chairmanHandler = new ChairmanHandler();

        // 组合处理者对象构造责任链
        departmentLeaderHandler.setNextHandler(managerHandler);
        managerHandler.setNextHandler(chairmanHandler);

        // 提交请求,调用责任链,调用责任链第一个处理者对象即可
        String result = departmentLeaderHandler.handleRequest("采购人员", 105000.0);
        System.out.println(result);
    }
}

当调用责任链处理请求时,并不知道谁会真正处理请求,只知道将请求提交到第一个处理者对象。从第一个处理者对象开始,整个责任链中的对象,要么自己处理请求,要么继续转发给下一个接收者。

从 if…else 判断到责任链模式的使用可以看出,代码结构变得清晰了。 责任链模式很好的满足了单一职责和开闭原则。

在责任链模式中,可能没有合适的处理者,请求不一定会被处理,所以可以在最后加上不支持处理该请求的提示。

责任链模式扩展

责任链构建方式

责任链构建方式多种多样,现在总结三种基本的实现方式:

1、客户端在提交请求前组合处理者构造责任链,即在使用的时候动态组合链。如前面责任链的实现就是这种。

2、可以在抽象处理者 Handler 内部实现责任链的组合。

3、由各个具体处理者自己决定后继者,这种方式要求每个处理者了解业务流程。

纯与不纯的责任链

纯的责任链模式

  • 纯的责任链模式的每个处理者对象只会做两件事,要么处理请求并返回,要么将请求传递给链上的下一个处理者对象。

不纯的责任链模式

  • 不满足纯的责任链模式要求的就是不纯的责任链,也可以称为功能链。一个请求在责任链中传递,每个处理者对象负责处理请求的某一方面的功能,处理完成后,不是停止,而是继续向后继者传递请求,后继者可以继续处理该请求。
  • 在实际应用开发中,进行业务处理之前,通常需要进行权限检查、通用数据校验、数据逻辑校验等处理,然后才开始真正的业务逻辑实现。可以把这些功能分散到一个功能链中。这样做的目的是使程序结构更加灵活,而且复用性会更好。比如通用的权限检查只需要做一份,然后就可以在多个功能链中使用了。

责任链模式的优缺点

优点:

  1. 请求者和处理者松散耦合

    在责任链模式中,请求者并不知道接收者是谁,也不知道具体如何处理,请求者只是负责向责任链发出请求就可以了。而每个处理者对象也不用管请求者或者是其他的处理者对象,只负责处理自己的部分,其他的就交给其他的处理者对象去处理。并且链中的对象不需要知道链的结构,可以由客户端负责链的创建,降低耦合。

  2. 动态组合

    责任链模式会把功能处理分散到单独的处理者对象中,然后在使用的时候,可以动态组合处理者形构造责任链,从而可以灵活地给对象分配职责,也可以灵活地实现和改变对象的职责。

  3. 符合开闭原则

    在系统中新增具体处理者对象时无需修改原代码,只需要在客户端重新构建责任链即可。

缺点:

  1. 产生很多细粒度对象

    责任链模式会把功能处理分散到单独的处理者对象中,也就是每个处理者对象只处理一个方面的功能,要把整个业务处理完,需要很多处理者对象的组合,这样会产生大量的细粒度处理者对象。

  2. 不一定能被处理

    责任链模式的每个处理者对象只负责自己处理的那一部分,因此可能会出现某个请求,把整个链传递完了,都没有处理者对象处理它。这就需要在使用责任链模式的时候,需要提供默认的处理,并且注意构建的链的有效性。

  3. 责任链模式可能会带来一些额外的性能损耗,因为它每次执行请求都要从链子开头开始遍历。甚至责任链构建不当,出现循环调用,导致系统陷入死循环。

责任链模式在框架源码中的应用

完整后续内容请关注公众号【Qin的学习营地】,回复:【责任链模式

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python 如何实现职责链设计模式?什么是职责链设计模式?Python 职责链设计模式示例代码

什么是职责链&#xff08;Chain of Responsibility&#xff09;设计模式&#xff1f; 职责链&#xff08;Chain of Responsibility&#xff09;设计模式是一种行为型设计模式&#xff0c;旨在构建一个对象链&#xff0c;每个对象都有机会处理请求&#xff0c;并且可以将请求传…

C++初阶 | [三] 类和对象(中)

摘要&#xff1a;类的6个默认成员函数&#xff0c;日期类 如果一个类中什么成员都没有&#xff0c;简称为空类。然而&#xff0c;空类并不是什么成员都没有&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成6个默认成员函数。默认成员函数&#xff1a;用户没有显式…

python中列表的基础解释

列表&#xff1a; 一种可以存放多种类型数据的数据结构 列表的创建&#xff1a; 1.用【】创建列表 #创建一个空列表 list1[] #创建一个非空列表 list2 [zhang,li,ying,1,2,3] #输出内容及类型 print(list1,type(list1)) print(list2,type(list2))结果&#xff1a; 2.使用list…

《视觉SLAM十四讲》-- 回环检测

文章目录 10 回环检测10.1 概述10.1.1 回环检测的意义10.1.2 回环检测的方法10.1.3 准确率和召回率 10.2 词袋模型10.3 字典10.3.1 字典的结构10.3.2 实践&#xff1a;创建字典 10.4 相似度计算10.4.1 理论部分10.4.2 实践&#xff1a;相似度的计算 10.5 实验分析与评述 10 回环…

股票价格预测 | Python实现基于CNN卷积神经网络的股票预测模型(keras,Conv1D)

文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 股票价格预测 | Python实现基于CNN卷积神经网络的股票预测模型(keras) 源码设计 import quandl import datetimedf = quandl

FreeRTOS(教程非常详细)

概述&#xff1a; 之前写了关于FreeRTOS的部分内容&#xff0c;为了方便阅读&#xff0c;现在给汇总到一起了。全部学习完后&#xff0c;恭喜你对FreeRTOS有了更深的认知。 第一章 FreeRTOS移植到STM32 第二章 FreeRTOS创建任务 第三章 FreeRTOS任务管理 第四章 FreeRTOS消…

LeetCode【12】整数转罗马数字

题目&#xff1a; 思路&#xff1a; https://blog.csdn.net/m0_71120708/article/details/128769894 代码&#xff1a; public String intToRoman(int num) {String[] thousands new String[] {"", "M", "MM", "MMM"};String[] hun…

【自然语言处理】【大模型】赋予大模型使用工具的能力:Toolformer与ART

赋予大模型使用工具的能力&#xff1a;Toolformer与ART ​ 本文介绍两种赋予大模型使用外部工具能力的方法&#xff1a;Toolformer和ART。 Toolformer论文地址&#xff1a;https://arxiv.org/pdf/2302.04761.pdf ART论文地址&#xff1a;https://arxiv.org/pdf/2303.09014.pd…

【网络奇遇记】那年我与计算机网络的浅相知

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. 计算机网络的定义1.1 计算机早期的一个最简单的定义1.2 现阶段计算机网络的一个较好的定义 二. …

【IPC】消息队列

1、IPC对象 除了最原始的进程间通信方式信号、无名管道和有名管道外&#xff0c;还有三种进程间通信方式&#xff0c;这 三种方式称之为IPC对象 IPC对象分类&#xff1a;消息队列、共享内存、信号量(信号灯集) IPC对象也是在内核空间开辟区域&#xff0c;每一种IPC对象创建好…

【汇编】处理字符问题

文章目录 前言一、处理字符问题1.1 汇编语言如何处理字符1.2 asciiascii码是什么&#xff1f;ascii码表是什么&#xff1f; 1.3 汇编语言字符示例代码 二、大小写转换2.1 问题&#xff1a;对datasg中的字符串2.2 逻辑与和逻辑或2.3 程序&#xff1a;解决大小写转换的问题一个新…

「项目阅读系列」go-gin-example star 6.5k!(1)

文章目录 准备工作适宜人群项目信息 项目结构代码阅读主要模块代码主函数模块router 路由模块auth 授权模块数据库 修改文章请求分析其他依赖 总结 准备工作 适宜人群 初学 go 语法&#xff0c;希望了解 go 项目的构建过程和方式。 项目信息 go-gin-example 项目是使用 gin…

qt-C++笔记之两个窗口ui的交互

qt-C笔记之两个窗口ui的交互 code review! 文章目录 qt-C笔记之两个窗口ui的交互0.运行1.文件结构2.先创建widget项目&#xff0c;搞一个窗口ui出来3.项目添加第二个widget窗口出来4.补充代码4.1.qt_widget_interaction.pro4.2.main.cpp4.3.widget.h4.4.widget.cpp4.5.second…

JAVA for 循环训练 Pattern

import java.util.Scanner;public class Pattern {public static void main(String[] args) {int[] arr {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0};Scanner in new Scanner(System.in);System.out.print("请输入n:");int n in.nextInt();in.close();for …

LeetCode27.移除元素(暴力法、快慢指针法)

每日一题&#xff1a;LeetCode27.移除元素 1.问题描述2.解题思路3.代码 1.问题描述 问题描述&#xff1a;给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。不要使用额外的数组空间&#xff0c;你必…

Linux(Ubuntu)安装JDK环境

系统环境 Ubuntu20.04 下载JDK压缩包 前往Oracle官网进行后续下载或单击下载JDK压缩包 下拉找到JDK8&#xff0c;在Linux板块下选择适配系统架构的压缩包文件(后缀为tar.gz)&#xff0c;系统架构可通过uname -m命令查看 安装JDK 安装环境通常放在/usr/local下&#xff0c;进入…

免费稳定几乎无门槛,我的ChartGPT助手免费分享给你

公众号「架构成长指南」&#xff0c;专注于生产实践、云原生、分布式系统、大数据技术分享。 概述 ChatGPT想必大家应该都不陌生了&#xff0c;大部分人或多或少都接触了&#xff0c;好多应该都是通过openAi的官方进行使用的&#xff0c;这个门槛对大部分人有点高&#xff0c;…

公共字段自动填充-@TableField的fill实现(2)

TheadLocal 客户端发送的每次http请求&#xff0c;在服务端都会分配新的线程。因此登录检查过滤器、controller、元数据对象处理器属于一个线程。 TheadLocal是线程的局部变量&#xff1a; TheadLocal常用方法&#xff1a; 如何在元数据对象处理器中获取当前登录用户的id&…

开发知识点-uniapp微信小程序-开发指南

uniapp uni.chooseLocationgetCurrentPages美团外卖微信小程序开发uniapp-美团外卖微信小程序开发P1 成果展示P2外卖小程序后端&#xff0c;学习给小程序写http接口P3 主界面配置P4 首页组件拆分P13 外卖列表布局筛选组件商家 布局测试数据创建样式 请求商家外卖数据封装请求并…

酷柚易汛ERP - 序列号盘点操作指南

1、应用场景 将系统中开启序列号的商品数量与与实际存放的数量进行对比。 2、主要操作 2.1 录入序列号 打开【盘点】-【序列号盘点】&#xff0c;新增序列号盘点单&#xff0c;点击【SN】按钮&#xff0c;在弹框中输入序列号。 支持扫描枪录入序列号支持复制粘贴序列号录入…