【设计模式深度剖析】【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. 读者:读者想要借阅一本书。他们首先会到自助借书机或前台服务台提出借阅请求。
  2. 自助借书机:这是第一个“处理者”。如果自助借书机正常工作且读者持有有效的借阅证,它可以直接处理借阅请求,打印出借阅凭条,并更新图书的借阅状态。
  3. 前台服务台:如果自助借书机出现故障或读者遇到问题,他们可以将借阅请求传递给前台服务台的工作人员。工作人员会检查读者的借阅证是否有效,并手动处理借阅请求。
  4. 图书管理员:如果前台服务台的工作人员发现图书已经被其他读者预约或存在其他需要管理员处理的问题(例如,图书需要修复或更新库存),他们会将借阅请求传递给图书管理员。图书管理员会进一步处理这些特殊情况,并决定是否批准借阅请求。
  5. 系统管理员:在某些情况下,例如系统出现严重故障或需要更改借阅规则时,图书管理员可能无法直接处理请求。这时,他们可以将请求传递给系统管理员,由系统管理员来处理这些系统级别的问题。

在这个场景中,每个角色(自助借书机、前台服务台、图书管理员、系统管理员)都是一个“处理者”,他们共同组成了一个“职责链”。当一个借阅请求出现时,它会沿着这个链传递,直到有一个“处理者”能够处理它为止

这种流程设计降低了各个角色之间的依赖和耦合,使得借阅流程更加灵活和高效。同时,它也提高了系统的可扩展性,因为新的处理者可以很容易地添加到链中,以处理新的请求或应对新的情况

在软件系统中,当有多个对象可以处理同一类请求时,使用职责链模式可以避免请求发送者和接收者之间的紧密耦合,使得系统更加灵活和可扩展

职责链模式的角色

职责链模式中的角色通常包括以下几种:

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 {
    @Override
    public 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 {
    @Override
    public 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 {
    @Override
    public 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,依此类推。

最终,所有的请求都会被处理或者因为找不到合适的处理者而结束。

职责链模式的应用

优点

  1. 降低耦合度:请求者和接收者之间不直接联系,降低了系统的耦合度。
  2. 增强系统的可扩展性可以根据需要增加新的请求处理类,满足开闭原则
  3. 增强给对象指派职责的灵活性当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任
  4. 责任链简化了对象之间的连接:每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。

缺点

  1. 不能保证每个请求一定被处理:由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理;一个请求也可能因为链的结构没有得到正确构建而得不到处理(比如忘记给处理者设置后继者)。
  2. 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响
  3. 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

使用场景

  1. 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定
  2. 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求
  3. 可动态指定一组对象处理请求,客户端可以动态地设置职责链来处理请求,也可以改变链内的成员或者调动它们的次序。

示例解析:购物中心客户服务流程

为了更直观地解释职责链模式,我们可以使用一个简单的例子:购物中心的客户服务流程。当顾客在购物中心遇到问题(如退换货、咨询商品信息等)时,他们可能会首先找到最近的店员寻求帮助。如果店员不能解决问题,他们可能会将问题转交给部门经理,如果部门经理也不能处理,问题最终可能会提交给客服中心。

生活中的例子

  1. 顾客:在购物中心遇到问题的顾客。
  2. 店员:首先接待顾客的人,可以处理一些简单的请求,如商品信息查询。
  3. 部门经理:如果店员不能解决问题,顾客的问题会转交给部门经理,部门经理能处理更复杂的请求,如退换货。
  4. 客服中心:如果部门经理也无法解决问题,问题最终会提交给客服中心。

类图

在这里插入图片描述

类图分析

抽象出抽象处理者 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;

    @Override
    public 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;
        }
    }

    @Override
    public 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;

    @Override
    public 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;
        }
    }

    @Override
    public 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 {
    @Override
    public String handleRequest(String request) {
        return "Customer Service Center handled request: " + request;
    }

    @Override
    public 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
*///~

在这个例子中,我们模拟了三种不同类型的顾客请求,并展示了它们如何通过职责链被不同的处理者处理。如果一个处理者不能处理请求,它会将请求传递给链中的下一个处理者,直到找到能够处理该请求的处理者,或者到达链的末尾。

---

👈️上一篇:命令模式

设计模式-专栏👈️

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

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

相关文章

Vue随笔记

1 Idea里面使用Vue Idea里面要安装Vue插件 File - New - Project - JavaScript - Vue.js 然后出现&#xff1a; "C:\Program Files\nodejs\node.exe" "C:\Program Files\nodejs\node_modules\npm\bin\npx-cli.js" --ignore-existing --package vue/cli…

学习串口屏需要了解哪些方面的知识

学习串口屏需要掌握的知识主要包括以下几个方面&#xff1a; 串口通信原理&#xff1a;串口屏是基于串口通信的显示控制模组&#xff0c;因此了解串口通信的基本原理和通信协议是必要的。你需要了解串口通信的基本概念、数据格式、波特率、校验位等参数&#xff0c;以及串口通…

信息学奥赛初赛天天练-21-完善程序-动态规划、编辑距离与字符数组应用的极致探索

PDF文档公众号回复关键字:20240606 1 2023 CSP-J 完善程序2 完善程序&#xff08;单选题&#xff0c;每小题 3 分&#xff0c;共计 30 分&#xff09; 给定两个字符串&#xff0c;每次操作可以选择删除&#xff08;Delete&#xff09;、插入&#xff08;Insert&#xff09;…

一文读懂动态IP与静态IP的区别与选择

在当今的数字世界中&#xff0c;网络安全与隐私保护已成为越来越重要的话题&#xff0c;网络代理作为一种常见的技术工具&#xff0c;被广泛应用于保护用户隐私、绕过网络限制、进行网络测试等诸多领域。 IPFoxy提供独享纯净的动态IP代理与静态IP代理选择&#xff0c;我们需要根…

AI大模型语料库

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 语料库概述 语料库&#xff08;Corpus&#xff09;是一个存储了大量真实语言使用实例的集合&#xff0c;这些实例可以是文本、语音、视频等多种形式的语言数据。语料库通常…

如何将 MySQL 数据库共享给他人?

文章目录 共享所有数据库给他人1. 连接到 MySQL 数据库2. 选择要使用的数据库3. 修改连接所需的 host4. 刷新权限 共享部分数据库给他人1. 创建用户2. 授权3. 刷新权限 结语 &#x1f389;欢迎来到Java学习路线专栏~探索Java中的静态变量与实例变量 ☆* o(≧▽≦)o *☆嗨~我是I…

HCIP-Datacom-ARST自选题库_10_其他判断【23道题】

1.端到端时延等于路径上所有处理时延与队列时延之和。 2.部署PPP Multilink之后&#xff0c;数据将根据源地址和目的地址均匀的分配在各条成员链路上。 3.流镜像分为本地流镜像和远程流镜像两种方式。√ 4.IP报文中用Tos字段进行Q0S标记&#xff0c;Tos字段中是使用前6bit来…

BGP基础实验

BGP协议中的建邻&#xff0c;与宣告路由分开的 在任何一台BGP路由上&#xff0c;均可宣告本地路由表中通过任何形势获取的路由条目&#xff0c;将其共享给其他BGP邻居&#xff1b; 然后display ip rou查看 *>代表状态 *的意思是可用 >代表优 i和*>无关&#x…

数据结构———链表

链表是经常用到的一种基础数据结构&#xff0c;接下来我们讲讲链表。 链表&#xff1a; 特点&#xff1a; 链表可分为有头/无头链表&#xff0c;循环/无环&#xff0c;双向/单向链表&#xff0c;每个链表节点都包含一个数据和下一个链表节点的地址。 每个链表节点都指向下一…

树-层序遍历序列构造二叉树(mid)

一、问题描述 二、实现思路 问题给出了层序遍历序列&#xff0c;我们使用队列来实现二叉树的构造过程&#xff1a; 这里注意&#xff1a;在写代码时&#xff0c;比较字符串数组内元素str和某个字符串是否相等时用str.equals("#")的操作&#xff0c;如果用 会引发比较…

上市即交付,比亚迪秦L DM-i万人交车暨千媒众测开营

6月6日&#xff0c;“引领中级 开创油耗2时代”秦L DM-i万人交车暨千媒众测开营仪式在比亚迪大本营深圳盛大举行。 众多车主代表亲临现场&#xff0c;与全国各地的比亚迪4S店千店联动&#xff0c;将秦L DM-i全国交付推向新的高潮。发布即量产&#xff0c;上市即交付&#xff0…

leetcode及牛客网二叉树相关题、单值二叉树、相同的树、二叉树的前序、中序、后序遍历、另一棵树的子树、二叉树的遍历、 对称二叉树等的介绍

文章目录 前言一、单值二叉树二、相同的树三、二叉树的前序遍历四、二叉树的中序遍历五、二叉树的后序遍历六、另一棵树的子树七、二叉树的遍历八、 对称二叉树总结 前言 leetcode及牛客网二叉树相关题、单值二叉树、相同的树、二叉树的前序、中序、后序遍历、另一棵树的子树、…

STM32项目分享:智能家居语音系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB打板焊接图: 五、程序设计 六、实验效果 七、包含内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.com…

删除的照片为什么总是反复出现?6种有效解决方案

在您使用手机时可能会遇到这样的问题&#xff1a;为什么删除了照片后它又回来了&#xff1f;这种奇怪的情况可能会让用户感到不安&#xff0c;并且质疑设备的可靠性。本文将解释导致这种现象的原因&#xff0c;并探讨确保永久删除图像的有效方法。让我们来解决“为什么我的照片…

【软件项目管理篇】怎样平衡软件质量与时间成本范围的关系?

你会发现&#xff0c;在实际的软件项目中不乏这样的例子&#xff1a; 一个项目&#xff0c;正常估算&#xff0c;要三个月才能完成&#xff0c;但是老板或客户要压缩到一个月完成&#xff0c;而你不知道如何说服他们&#xff1b;项目开发一半&#xff0c;产品经理告诉你&#…

智能电销机器人的作用和原理是什么?

要问世界上更火爆的创新技术&#xff0c;人工智能必然要算其一&#xff0c;人工智能正不断的改变着我们的生活&#xff0c;比如智能手机、智能家居、智能门锁等产品已经不断的渗透在了我们的生活之中&#xff0c;而近几年兴起的人工智能语音识别机器人&#xff0c;也迅速俘获了…

【蓝桥杯2025备赛】分巧克力

【蓝桥杯2025备赛】分巧克力 [蓝桥杯 2017 省 AB] 分巧克力 题目描述 儿童节那天有 K K K 位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。 小明一共有 N N N 块巧克力&#xff0c;其中第 i i i 块是 H i W i H_i \times W_i Hi​Wi​ 的方格组成的长方形…

GPT、Claude、Perplexity等AI集体宕机罢工,全球打工人崩溃了

就在昨天&#xff01;一个看似平常的周三上午&#xff0c;三大顶尖AI居然集体罢工了&#xff01; 首先&#xff0c;网友们发现OpenAI的ChatGPT崩了&#xff0c;接着Claude和Perplexity也接连陷入崩溃状态。而Gemini也出现了短暂下线问题&#xff0c;整整三个小时&#xff0c;这…

手写节流throttle

节流throttle 应用场景 滚动事件监听scroll&#xff1a;例如监听页面滚动到底部加载更多数据时&#xff0c;使用节流技术减少检查滚动位置的频率&#xff0c;提高性能。鼠标移动事件mousemove&#xff1a;例如实现一个拖拽功能&#xff0c;使用节流技术减少鼠标移动事件的处理…

封装了一个仿照抖音评论轮播效果的iOS轮播视图

效果图 原理 就是我们在一个视图里面有两个子视图&#xff0c;一个是currentView, 一个是willShowView,在一次动画过程中&#xff0c;我们改变current View的frame&#xff0c;同时改变willShowView的frame&#xff0c;同时&#xff0c;需要改变currentVIew 的transform.y不然…