【设计模式深度剖析】【3】【行为型】【职责链模式】| 以购物中心客户服务流程为例加深理解
👈️上一篇:命令模式 | 下一篇:策略模式👉️
设计模式-专栏👈️
文章目录
- 职责链模式
- 定义
- 英文原话
- 直译
- 如何理解呢?
- 职责链模式的角色
- 1. Handler(抽象处理者)
- 2. ConcreteHandler(具体处理者)
- 3. Client(客户类)
- 类图
- 类图分析
- 代码示例
- 职责链模式的应用
- 优点
- 缺点
- 使用场景
- 示例解析:购物中心客户服务流程
- 类图
- 类图分析
- 代码示例
职责链模式
职责链模式(Chain of Responsibility Pattern)是一种常见的行为模式。
职责链模式是一种将多个对象链接起来以处理相同请求的设计模式,就像一条流水线或接力棒传递,每个对象都有机会处理请求,如果不能处理则传递给下一个对象,直到找到能够处理的对象或传递完毕。
这种模式降低了对象间的耦合度,增强了系统的可扩展性和灵活性,使得请求的处理过程更加清晰和模块化。
简而言之,职责链模式==让请求在多个对象间“接力传递”,直到找到“合适的人”==来处理。
定义
英文原话
Chain of Responsibility Pattern: Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
直译
职责链模式:通过将多个对象连接成一条链,并沿着这条链传递请求,以避免请求的发送者与接收者之间的紧密耦合。直到链上的某个对象处理请求为止。
如何理解呢?
想象一下在图书馆借阅图书的流程。在一个大型的图书馆系统中,处理图书借阅的流程可能涉及到多个步骤和角色,每个角色都有自己特定的职责。
- 读者:读者想要借阅一本书。他们首先会到自助借书机或前台服务台提出借阅请求。
- 自助借书机:这是第一个“处理者”。如果自助借书机正常工作且读者持有有效的借阅证,它可以直接处理借阅请求,打印出借阅凭条,并更新图书的借阅状态。
- 前台服务台:如果自助借书机出现故障或读者遇到问题,他们可以将借阅请求传递给前台服务台的工作人员。工作人员会检查读者的借阅证是否有效,并手动处理借阅请求。
- 图书管理员:如果前台服务台的工作人员发现图书已经被其他读者预约或存在其他需要管理员处理的问题(例如,图书需要修复或更新库存),他们会将借阅请求传递给图书管理员。图书管理员会进一步处理这些特殊情况,并决定是否批准借阅请求。
- 系统管理员:在某些情况下,例如系统出现严重故障或需要更改借阅规则时,图书管理员可能无法直接处理请求。这时,他们可以将请求传递给系统管理员,由系统管理员来处理这些系统级别的问题。
在这个场景中,每个角色(自助借书机、前台服务台、图书管理员、系统管理员)都是一个“处理者”,他们共同组成了一个“职责链”。当一个借阅请求出现时,它会沿着这个链传递,直到有一个“处理者”能够处理它为止。
这种流程设计降低了各个角色之间的依赖和耦合,使得借阅流程更加灵活和高效。同时,它也提高了系统的可扩展性,因为新的处理者可以很容易地添加到链中,以处理新的请求或应对新的情况。
在软件系统中,当有多个对象可以处理同一类请求时,使用职责链模式可以避免请求发送者和接收者之间的紧密耦合,使得系统更加灵活和可扩展。
职责链模式的角色
职责链模式中的角色通常包括以下几种:
1. Handler(抽象处理者)
定义一个处理请求的接口
(可选) 实现后继链
定义一个处理请求的接口,通常包含一个方法用于处理请求和一个属性用于保存对下一个处理者的引用。
2. ConcreteHandler(具体处理者)
- 处理它所负责的请求
- 可访问它的后继者
- 如果可处理该请求,就处理;否则将该请求转发给它的后继者
实现抽象处理者接口,处理它所负责的请求;如果可以处理该请求就处理,否则将该请求传给它的后继者。
3. Client(客户类)
设置职责链
向链上的具体处理者(ConcreteHandler)对象提交请求
创建处理链,并向链的第一个处理者对象发送请求。
类图
类图分析
抽象处理者组合了自身类型的对象(定义了 successor
后继处理器属性并通过setSuccssor()
方法进行赋值),体现在子类上就是每个具体的处理者可以设置后继处理器,即当前无法处理的话,递给后继处理器进行处理。
代码示例
下面是一个简单的Java示例,演示了职责链模式的应用:
抽象处理者
abstract class Handler {protected Handler successor; // 持有后继者的引用 // 设置后继者 public void setSuccessor(Handler successor) {this.successor = successor;}// 处理请求的方法(声明为抽象方法,由具体处理者实现) public abstract void handleRequest(int request);
}
具体处理者A
class ConcreteHandlerA extends Handler {@Overridepublic void handleRequest(int request) {if (request >= 0 && request < 10) {System.out.println("Handler A handled request " + request);} else if (successor != null) {successor.handleRequest(request); // 如果不能处理,则传递给后继者 }}
}
具体处理者B
class ConcreteHandlerB extends Handler {@Overridepublic void handleRequest(int request) {if (request >= 10 && request < 20) {System.out.println("Handler B handled request " + request);} else if (successor != null) {successor.handleRequest(request); // 如果不能处理,则传递给后继者 }}
}
具体处理者C
class ConcreteHandlerC extends Handler {@Overridepublic void handleRequest(int request) {if (request >= 20) {System.out.println("Handler C handled request " + request);} else {// 这里没有后继者,请求到此为止 System.out.println("No handler could process the request " + request);}}
}
客户类
package com.polaris.designpattern.list3.behavioral.pattern03.chainofresponsibility.classicdemo;public class Client {public static void main(String[] args) {Handler handlerA = new ConcreteHandlerA();Handler handlerB = new ConcreteHandlerB();Handler handlerC = new ConcreteHandlerC();// 设置职责链 handlerA.setSuccessor(handlerB);handlerB.setSuccessor(handlerC);// 发送请求 int[] requests = {2, 15, 30};for (int request : requests) {handlerA.handleRequest(request);}}
}
/* Output:
Handler A handled request 2
Handler B handled request 15
Handler C handled request 30
*///~
在上面的示例中,我们定义了三个具体处理者(A、B、C),它们分别处理不同范围的请求。在客户类中,我们创建了一个处理链,并将请求发送到链的第一个处理者(A)。如果A不能处理请求,它会将请求传递给B,依此类推。
最终,所有的请求都会被处理或者因为找不到合适的处理者而结束。
职责链模式的应用
优点
- 降低耦合度:请求者和接收者之间不直接联系,降低了系统的耦合度。
- 增强系统的可扩展性:可以根据需要增加新的请求处理类,满足开闭原则。
- 增强给对象指派职责的灵活性:当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
- 责任链简化了对象之间的连接:每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
缺点
- 不能保证每个请求一定被处理:由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理;一个请求也可能因为链的结构没有得到正确构建而得不到处理(比如忘记给处理者设置后继者)。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
使用场景
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可动态指定一组对象处理请求,客户端可以动态地设置职责链来处理请求,也可以改变链内的成员或者调动它们的次序。
示例解析:购物中心客户服务流程
为了更直观地解释职责链模式,我们可以使用一个简单的例子:购物中心的客户服务流程。当顾客在购物中心遇到问题(如退换货、咨询商品信息等)时,他们可能会首先找到最近的店员寻求帮助。如果店员不能解决问题,他们可能会将问题转交给部门经理,如果部门经理也不能处理,问题最终可能会提交给客服中心。
生活中的例子:
- 顾客:在购物中心遇到问题的顾客。
- 店员:首先接待顾客的人,可以处理一些简单的请求,如商品信息查询。
- 部门经理:如果店员不能解决问题,顾客的问题会转交给部门经理,部门经理能处理更复杂的请求,如退换货。
- 客服中心:如果部门经理也无法解决问题,问题最终会提交给客服中心。
类图
类图分析
抽象出抽象处理者 CustomerServiceRequestHandler
客户服务请求处理器,
店员 Clerk
, 部门经理 DepartmentManager
, 客服中心 CustomerServiceCenter
分别对其进行实现,是请求的具体处理者,
从类图也可发现,店员Clerk
对象组合了一个请求处理器对象,即后继处理节点,在这个示例是部门经理DepartmentManager
对象,表示店员如果可以处理的客户问题,店员就进行处理,否则交给他的后继节点部门经理处理;
如果请求被流转到部门经理DepartmentManager
对象来处理,如果他能处理,则自行处理返回了,否则将客户的问题递给后继节点处理,这里是客服中心CustomerServiceCenter
对象,从类图可以看出部门经理对象组合了一个客户服务请求处理器,具体是客服中心对象。
在本示例中,任何店员,部门经理处理不了的问题,客服中心都负责处理,他是最后的问题处理节点,因此它不再组合后继节点。
代码示例
首先,我们定义一个处理请求的接口
public interface CustomerServiceRequestHandler {String handleRequest(String request);void setNextHandler(CustomerServiceRequestHandler nextHandler);
}
然后,我们创建实现该接口的类来表示不同的处理者:
1.店员 Clerk
public class Clerk implements CustomerServiceRequestHandler {private CustomerServiceRequestHandler nextHandler;@Overridepublic String handleRequest(String request) {if (canHandleRequest(request)) {return "Clerk handled request: " + request;} else if (nextHandler != null) {return nextHandler.handleRequest(request);} else {return "No handler could process the request: " + request;}}@Overridepublic void setNextHandler(CustomerServiceRequestHandler nextHandler) {this.nextHandler = nextHandler;}private boolean canHandleRequest(String request) {// 假设店员只能处理商品信息查询请求return request.startsWith("Product Info");}
}
2.部门经理 DepartmentManager
public class DepartmentManager implements CustomerServiceRequestHandler {private CustomerServiceRequestHandler nextHandler;@Overridepublic String handleRequest(String request) {if (canHandleRequest(request)) {return "Department Manager handled request: " + request;} else if (nextHandler != null) {return nextHandler.handleRequest(request);} else {return "No handler could process the request: " + request;}}@Overridepublic void setNextHandler(CustomerServiceRequestHandler nextHandler) {this.nextHandler = nextHandler;}private boolean canHandleRequest(String request) {// 假设部门经理可以处理退货请求return request.startsWith("Return an item");}
}
3.客服中心 CustomerServiceCenter
public class CustomerServiceCenter implements CustomerServiceRequestHandler {@Overridepublic String handleRequest(String request) {return "Customer Service Center handled request: " + request;}@Overridepublic void setNextHandler(CustomerServiceRequestHandler nextHandler) {// 客服中心是链的末尾,不需要设置下一个处理者// 这里可以抛出一个异常或者忽略这个调用}
}
最后,我们可以创建一个客户端类来演示如何使用这个职责链
public class CustomerServiceChainDemo {public static void main(String[] args) {// 创建处理者实例CustomerServiceRequestHandler clerk = new Clerk();CustomerServiceRequestHandler manager = new DepartmentManager();CustomerServiceRequestHandler center = new CustomerServiceCenter();// 构建职责链clerk.setNextHandler(manager);manager.setNextHandler(center);// 模拟顾客提交请求//查询商品信息String request1 = "Product Info 123";String result1 = clerk.handleRequest(request1);System.out.println(result1); // 输出:Clerk handled request: Product Info 123//退货String request2 = "Return an item";String result2 = clerk.handleRequest(request2);System.out.println(result2); // 输出:Department Manager handled request: Return an item//服务投诉String request3 = "Complaint about service";String result3 = clerk.handleRequest(request3);System.out.println(result3); // 输出:Customer Service Center handled request: Complaint about service}
}
/* Output:
Clerk handled request: Product Info 123
Department Manager handled request: Return an item
Customer Service Center handled request: Complaint about service
*///~
在这个例子中,我们模拟了三种不同类型的顾客请求,并展示了它们如何通过职责链被不同的处理者处理。如果一个处理者不能处理请求,它会将请求传递给链中的下一个处理者,直到找到能够处理该请求的处理者,或者到达链的末尾。
👈️上一篇:命令模式 | 下一篇:策略模式👉️
设计模式-专栏👈️
相关文章:

【设计模式深度剖析】【3】【行为型】【职责链模式】| 以购物中心客户服务流程为例加深理解
👈️上一篇:命令模式 | 下一篇:策略模式👉️ 设计模式-专栏👈️ 文章目录 职责链模式定义英文原话直译如何理解呢? 职责链模式的角色1. Handler(抽象处理者)2. ConcreteHandler(具体处理者…...
评价GPT-4的方案
评价GPT-4的方案 引言: 随着人工智能技术的不断发展,自然语言处理领域取得了显著的突破。其中,GPT-4作为最新的大型语言模型之一,备受关注。本方案旨在对GPT-4进行全面评价,包括其技术特点、性能表现、应用场景以及潜在的影响等方面。 一、技术特点 1. 模型规模和参数数…...

LeetCode | 1624.两个相同字符之间的最长子字符串
这道题拿到手想法就是去双重遍历暴力解,对于每个字符,从后往前遍历字符串,找到从后往前一直到本次遍历的这个字符串这段子串中和这个字符串相同的字符位置,然后得到子字符串的长度,和ans存储的值做一个比较,…...

【CS.AI】GPT-4o:重新定义人工智能的新标杆
文章目录 1 序言2 GPT-4o的技术亮点3 GPT-4o与前代版本的对比3.1 热门AI模型对比表格GPT-3.5GPT-4GPT-4oBERTT5 3.2 其他 4 个人体验与感受5 结论 1 序言 嘿,大家好!今天要聊聊一个超级酷的AI新突破——GPT-4o!最近,OpenAI发布了…...

野火FPGA跟练(四)——串口RS232、亚稳态
目录 简介接口与引脚通信协议亚稳态RS232接收模块模块框图时序波形RTL 代码易错点Testbench 代码仿真 RS232发送模块模块框图时序波形RTL 代码Testbench 代码仿真 简介 UART:Universal Asynchronous Receiver/Transmitter,异步串行通信接口。发送数据时…...

Qt for Android 申请摄像头权限
步骤 1. 添加用户权限 方式1: AndroidManifest.xml 中新增(不添加后面申请选项时不弹窗) 或者再Qt Creator中直接添加 方式2: .pro 中引用multimedia 模块,编译时配置自动添加 <uses-permission android:name"android.permissi…...

kivy 百词斩项目 报错
AttributeError: FigureCanvasKivyAgg object has no attribute resize_event AttributeError: FigureCanvasKivyAgg object has no attribute resize_event 是一种常见的Python错误,当你试图访问一个对象(在这个例子中是 FigureCanvasKivyAgg 对象&am…...

ChatTTS 文字生成语言本地模型部署
ChatTTS部署 官方信息 [ChatTTS首页](https://chattts.com/)搭建步骤 1、下载源码 git clone https://github.com/2noise/ChatTTS.git 2、按照环境 pip install torch ChatTTS pip install -r requirements.txt 3、下载模型 git clone https://www.modelscope.cn/pzc163/ch…...

多曝光融合算法(三)cv2.createAlignMTB()多曝光图像融合的像素匹配问题
文章目录 1.cv2.createAlignMTB() 主要是计算2张图像的位移,假设位移移动不大2.多曝光图像的aline算法:median thresold bitmap原理讲解3.图像拼接算法stitch4.多曝光融合工具箱 1.cv2.createAlignMTB() 主要是计算2张图像的位移,假设位移移动…...
C/C++|类型推导中的模式匹配
在C11及以上的相关语法中,特别是在模版元编程的范式里,类型推导是了重中之重。 在 《Effective Modern C 》 中第一章主要就是讲各种类型推导。 当然了,谈到类型推导,我们不得不先搞懂类型推导中的模式匹配,这是基础&a…...

The 18th Northeast Collegiate Programming Contest(5/9/13)
心得 赛中ac:5,目前ac:9,题目总数:13 中档可做题还是很多的,可惜遇到了难绷的queueforces, 最后15min才判出来,oi赛制5wa4遗憾离场,赛后把几个题都给调过了࿰…...

Vue前端在线预览文件插件
Vue前端在线预览文件插件 一、使用场景 1.1.像文档资料等,只想让他人在线预览,但不能下载。此等场景需求可以用到此插件。 二、此文档介绍两种插件 1.view.xdocin插件 (上线后免费几天,然后收费,添加作者后,可以延…...
【ai】Audio2Face
Audio2Face 简介 Audio2Face是英伟达Omniverse平台的一部分,它使用先进的AI技术来生成基于音频输入的逼真面部动画。这个技术主要利用深度学习模型来解析人声,进而驱动一个三维模型的面部表情。下面是Audio2Face工作流程的详细说明: 预备阶段 在使用Audio2Face之前,需要准…...
2024.6.9 一
装饰器(Decorators) 装饰器是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,我们在用到装饰器时,常用到一个的符号,这个叫做语法糖,在函数定义前加上decorator_name, 那么后面的函数执行…...

地图之战争迷雾/地图算法/自动导航(一)
战争迷雾 TiledMap 创建黑色覆盖块,然后使用碰撞组件,控制黑色块的显示和隐藏 地图算法 在有些游戏中,地图需要随机生成,比如游戏中的迷宫等,这就需要地图生成的算法;在角色扮演类游戏中,角色…...

【wiki知识库】06.文档管理页面的添加--前端Vue部分
📝个人主页:哈__ 期待您的关注 目录 一、🔥今日目标 二、🐻前端Vue模块的改造 BUG修改 1.wangeditor无法展示问题 2.弹窗无法正常关闭问题 2.1 添加admin-doc.vue 2.1.1 点击admin-ebook中的路由跳转到admin-doc 2.2.2 进入…...
新电脑必装的7款软件,缺一不可
如果你买了新电脑或者是重装了新系统,那么这7款软件你一定要安装。 1、SpaceSniffer 如果你的C盘经常爆红,但是不知道是什么原因,那么你应该需要SpaceSniffer这款软件,它可以把你C盘中文件的空间占用情况,以大小方框…...

程序员学习Processing和TouchDesigner视觉编程相关工具
Proessing Processing 是一种用于视觉艺术和创意编程的开发环境和编程语言。它最初是为了帮助非专业程序员学习编程,特别是那些对于创意编程和视觉表达感兴趣的人。Processing 提供了简单易用的 API,使得绘制图形、创建动画和交互式应用变得相对容易。 …...

gitlabcicd-k8s部署gitlab
一.安装准备环境 存储使用nfs挂载持久化 k8s环境 helm安装 建议helm 3 二.部署gitlab-deploy.yaml nfs的ip是192.168.110.190 挂载目录是/data/data 注意所需要的目录需要创建:/data/data/gitlab/config ,/data/data/gitlab/logs ,/dat…...
浅谈JDBC
文章目录 一、什么是 JDBC?二、JDBC 操作流程三、JDBC代码例子 一、什么是 JDBC? JDBC是一种可用于执行SQL语句的JAVA API,是链接数据库和JAVA应用程序的纽带。JDBC一般需要进行3个步骤:与数据库建立一个链接、向数据库发送SQL语…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...

高考志愿填报管理系统---开发介绍
高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发,采用现代化的Web技术,为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## 📋 系统概述 ### 🎯 系统定…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...

李沐--动手学深度学习--GRU
1.GRU从零开始实现 #9.1.2GRU从零开始实现 import torch from torch import nn from d2l import torch as d2l#首先读取 8.5节中使用的时间机器数据集 batch_size,num_steps 32,35 train_iter,vocab d2l.load_data_time_machine(batch_size,num_steps) #初始化模型参数 def …...
Neo4j 完全指南:从入门到精通
第1章:Neo4j简介与图数据库基础 1.1 图数据库概述 传统关系型数据库与图数据库的对比图数据库的核心优势图数据库的应用场景 1.2 Neo4j的发展历史 Neo4j的起源与演进Neo4j的版本迭代Neo4j在图数据库领域的地位 1.3 图数据库的基本概念 节点(Node)与关系(Relat…...