设计模式第六讲:责任链模式和迭代器模式详解
一. 责任链模式
1. 背景
在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据自己要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。这样的例子还有很多,如找领导出差报销、生活中的“击鼓传花”游戏等。
2. 定义和特点
(1). 定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
PS:责任链模式也叫职责链模式。在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了。
(2). 优点:
A. 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
B. 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
C. 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
D. 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
E. 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
(3). 缺点:
A. 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
B. 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
C. 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
3. 具体实现
(1). 模式结构
A. 抽象处理者:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
B. 具体处理者:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
C. 客户端调用:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
结构图如下:

(2). 使用场景
员工请假,不同的天数需要被不同级别的领导批准,这个时候可以采用责任链模式。每个领导都是一个具体的请假处理者,同时提炼出来一个抽象处理者,包括:请假处理方法和下一个节点,最后由客户端建立责任链,并将请求传递进去。
更多C++后台开发技术点知识内容包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,音视频开发,Linux内核,TCP/IP,协程,DPDK多个高级知识点。
C/C++Linux服务器开发高级架构师/C++后台开发架构师免费学习地址
【文章福利】另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以点击领取

(3). 代码实操
请假请求类:
/// <summary>/// 请假请求类/// </summary>public class LeaveRequest{/// <summary>/// 姓名/// </summary>public string name { get; set; }/// <summary>/// 请假天数/// </summary>public int leaveDays { get; set; }}抽象处理者和具体处理者:
/// <summary>/// 抽象处理者角色/// </summary>public abstract class AbstractHandler{/// <summary>/// 代表责任链中的下一个角色/// </summary>public AbstractHandler NextHandler { get; set; }/// <summary>/// 处理请假请求/// </summary>public abstract void HandleRequest(LeaveRequest request);}/// <summary>/// 直接领导(一级领导)/// </summary>public class LeaderHandler1 : AbstractHandler{/// <summary>/// 处理请假请求/// </summary>public override void HandleRequest(LeaveRequest request){if (request.leaveDays<=3){Console.WriteLine($"直接领导(一级领导)已经批准{request.name}的请假请求,请假天数:{request.leaveDays}");}else{if (this.NextHandler!=null){this.NextHandler.HandleRequest(request);}else{Console.WriteLine($"{request.name}请假天数太多,没有人批准该假条!");}}}}/// <summary>/// 二级领导/// </summary>public class LeaderHandler2 : AbstractHandler{/// <summary>/// 处理请假请求/// </summary>public override void HandleRequest(LeaveRequest request){if (request.leaveDays > 3 && request.leaveDays <= 7){Console.WriteLine($"二级领导已经批准{request.name}的请假请求,请假天数:{request.leaveDays}");}else{if (this.NextHandler != null){this.NextHandler.HandleRequest(request);}else{Console.WriteLine($"{request.name}请假天数太多,没有人批准该假条!");}}}}/// <summary>/// 最高领导(三级)/// </summary>public class LeaderHandler3 : AbstractHandler{/// <summary>/// 处理请假请求/// </summary>public override void HandleRequest(LeaveRequest request){if (request.leaveDays > 7 && request.leaveDays <= 30){Console.WriteLine($"最高领导(三级)已经批准{request.name}的请假请求,请假天数:{request.leaveDays}");}else{if (this.NextHandler != null){this.NextHandler.HandleRequest(request);}else{Console.WriteLine($"{request.name}请假天数太多,没有人批准该假条!");}}}}
客户单创建责任链并调用:
//1. 有人要请假LeaveRequest myRequest1 = new LeaveRequest() { name="小明",leaveDays=6};LeaveRequest myRequest2 = new LeaveRequest(){name = "小王",leaveDays = 2};LeaveRequest myRequest3 = new LeaveRequest(){name = "小放",leaveDays = 15};LeaveRequest myRequest4 = new LeaveRequest(){name = "张三",leaveDays = 35};//2. 请假相关的责任链LeaderHandler1 handler1 = new LeaderHandler1();LeaderHandler2 handler2 = new LeaderHandler2();LeaderHandler3 handler3 = new LeaderHandler3();handler1.NextHandler = handler2;handler2.NextHandler = handler3;//3.进行请假处理handler1.HandleRequest(myRequest1);handler1.HandleRequest(myRequest2);handler1.HandleRequest(myRequest3);handler1.HandleRequest(myRequest4);测试结果:

4. 适用场景分析
(1). 有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
(2). 可动态指定一组对象处理请求,或添加新的处理者。
(3). 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
二. 迭代器模式
1. 背景
在现实生活以及程序设计中,经常要访问一个聚合对象中的各个元素,如【数据结构】中的链表遍历,通常的做法是将链表的创建和遍历都放在同一个类中,但这种方式不利于程序的扩展,如果要更换遍历方法就必须修改程序源代码,这违背了 “开闭原则”。既然将遍历方法封装在聚合类中不可取,那么聚合类中不提供遍历方法,将遍历方法由用户自己实现是否可行呢?答案是同样不可取,因为这种方式会存在两个缺点:
A. 暴露了聚合类的内部表示,使其数据不安全;
B. 增加了客户的负担。
“迭代器模式”能较好地克服以上缺点,它在客户访问类与聚合类之间插入一个迭代器,这分离了聚合对象与其遍历行为,对客户也隐藏了其内部细节,且满足“单一职责原则”和“开闭原则”。比如C#中的List就实现了迭代器模式。
2. 定义和特点
(1). 定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
(2). 优点:
A. 访问一个聚合对象的内容而无须暴露它的内部表示。
B. 遍历任务交由迭代器完成,这简化了聚合类。
C. 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
D. 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
E. 封装性良好,为遍历不同的聚合结构提供一个统一的接口。
(3). 缺点:
增加了类的个数,这在一定程度上增加了系统的复杂性
3. 具体实现
(1). 模式结构
迭代器模式是通过将聚合对象的遍历行为分离出来,抽象成迭代器类来实现的,其目的是在不暴露聚合对象的内部结构的情况下,让外部代码透明地访问聚合的内部数据。
A. 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
B. 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
C. 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
D. 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
结构如下图:

(2). 使用场景
见下面代码模拟。
(3). 代码实操
聚合接口和具体的聚合类
/// <summary>/// 抽象聚合接口/// </summary>public interface IAggregate<T>{public void Add(T obj);public void Remove(T obj);/// <summary>/// 获取聚合对象的迭代器/// </summary>/// <returns></returns>public Iterator<T> GetIterator();}/// <summary>/// 具体聚合/// </summary>public class ConcreteAggregate<T> : IAggregate<T>{private List<T> list = new List<T>();public void Add(T obj){list.Add(obj);}public void Remove(T obj){list.Remove(obj);}/// <summary>/// 获取迭代器/// </summary>/// <returns></returns>public Iterator<T> GetIterator(){return new ConcreteIterator<T>(list);}}抽象迭代器和具体的迭代器
/// <summary>/// 抽象迭代器/// </summary>public interface Iterator<T>{T First();T Next();bool HasNext();}/// <summary>/// 具体的迭代器/// </summary>/// <typeparam name="T"></typeparam>public class ConcreteIterator<T> : Iterator<T>{private List<T> list = null;private int index = 0;public ConcreteIterator(List<T> myList){this.list = myList;}public T First(){return list[0];}public bool HasNext(){if (index<list.Count-1){return true;}else{return false;}}public T Next(){T obj =default(T);if (this.HasNext()){obj = list[++index];}return obj;}}测试
{//聚合对象添加数据IAggregate<string> ag = new ConcreteAggregate<string>();ag.Add("001");ag.Add("002");ag.Add("003");ag.Add("004");//获取对应的迭代器Iterator<string> it = ag.GetIterator();//输出数据Console.WriteLine($"第一个元素为:{it.First()}");Console.WriteLine("剩下的元素为:");while (it.HasNext()){Console.WriteLine(it.Next());}
}运行结果

4. 适用场景分析
(1). 当需要为聚合对象提供多种遍历方式时。
(2). 当需要为遍历不同的聚合结构提供一个统一的接口时。
(3). 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。
PS: 由于聚合与迭代器的关系非常密切,所以大多数语言在实现聚合类时都提供了迭代器类,因此大数情况下使用语言中已有的聚合类的迭代器就已经够了。
原文链接:https://www.cnblogs.com/yaopengfei/p/13489597.html
相关文章:
设计模式第六讲:责任链模式和迭代器模式详解
一. 责任链模式1. 背景在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批…...
K8s 架构简介(一)
一、前言 在开始学习K8s之前,让我们对容器有一个基本的了解 1.1 什么是容器 一个容器镜像是一个可运行的软件包,其中包含了一个完整的可执行程序,包括代码和运行时需要应用、系统库和全部重要设置的默认值。 通过将应用程序本身ÿ…...
xshell6运行报错:由于找不到mfc110u.dll、MSVCR110.dll无法继续执行代码
今天给大家分享一下我刚装完系统遇到得问题,由于新盟的罗建雨【胡巴】老师帮我给电脑加了固态,又重装了系统,因此电脑里面得所有软件需要重装,在我重装的过程中遇到了一个小问题给大家分享一下,如果大家以后遇到也方便解决。 问题: 安装Xshell时电脑系统报错:“由于找…...
Baklib知识库管理平台,协助组织提升知识管理水平
随着信息时代和知识经济时代的到来,企业内部信息资料繁多冗杂,知识管理逐渐成为各大企业的重要工作之一,企业管理者无不感受到巨大的压力,怎么样将知识进行有效的管理,成为一个难点,并且随着信息不断的更迭…...
一文搞懂core-scheduling核心机制
cookie的原理借助于unsigned long型,和refcount_t引用计数器。 32位64位char *4字节8字节unsigned long4字节8字节 数据结构修改 首先看看实现core scheduling功能对数据结构有哪些修改 task_struct struct task_struct{struct rb_node core_node;unsigned long…...
IP地址在金融行业有哪些应用?
中国加入WTO以来经济得到迅速发展,金融行业随着经济发展体系越来越完善。随着西方金融公司和理念的加入中国金融行业开始多样化发展。金融行业在快速发展的同时也引发了许多弊端。如何维护挖掘客户更大需求?如何获取更多优质客户?如何提升网络…...
GT-suite v2016解决许可证过期问题(附新版liscense下载地址)
安装GT-suite v2016时遇到了如图报错的问题。当时的报错找不到了,下图是贴吧相同问题的报错图。 为了解决问题,先根据某网友的如下答复操作: 添加环境变量后仍然有相同报错。 看来需要寻找其他方法。 再尝试着卸载GT-suite v2016,…...
小红书商业笔记与普通笔记区别是什么?小红书笔记有哪几种
主攻单一平台,如何迅速打造爆文。针对软文发布类别的选择,小红书商业笔记与普通笔记区别究竟是什么,今天为大家带来的详细分析,告诉你该如何用最少的成本,做出“爆文”。1、小红书的笔记类型我们都知道,小红…...
DataWhale-统计学习方法打卡Task01
学习教材《统计学习方法(第二版)》李航 统计学习方法(第2版) by...李航 (z-lib.org).pdf https://www.aliyundrive.com/s/maJZ6M9hrTe 点击链接保存,或者复制本段内容,打开「阿里云盘」APP ,无…...
Java面试——Spring 事务
目录 1.什么是Spring 事务 2.Spring 事务的开启方式 3.Spring事务的实现方式/原理 4.事务传播机制 5.事务隔离级别 6.事务失效的原因 1.什么是Spring 事务 事务在逻辑上是一组操作,要么执行,要不都不执行。 如下: Begin; insert into…...
Python语言零基础入门教程(十九)
Python 异常处理 python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误。你可以使用该功能来调试python程序。 1、异常处理 2、断言(Assertions) python标准异常 什么是异常? 异常即是一个事件,该事件会在程序执行过程中发生&…...
重生之我是赏金猎人-SRC漏洞挖掘(一)-某SRC测试系统无脑Getshell
0x01 前言 https://github.com/J0o1ey/BountyHunterInChina 欢迎大佬们点个star 0x02 资产收集到脆弱系统 在某src挖掘过程中,本人通过ssl证书对域名资产进行了收集,通过计算域名对应ip段的权重 整理出其C段资产,进行了批量目录扫描 查看…...
Sciter 结合 PReact 实现组件公共逻辑抽离
Sciter 结合 PReact 实现组件公共逻辑抽离 下面例子是获取鼠标移动位置,将这部分逻辑进行抽离 一、使用高阶组件抽离公共逻辑 import {Component } from ./preact.js; export const HOCFactory = (Component) => {class HOC...
OpenTracing协议规范链接
一、官网链接 OpenTracing specificationhttps://opentracing.io/specification/不过目前OpenTracing项目已归档,不再维护。需要参考OpenTelemetry官网链接 Migrating from OpenTracing | OpenTelemetryBackward compatibility with OpenTracing has been a prior…...
金三银四面试必看,自动化测试如何解决日志问题
前言 前几天在员群里,有同学问了一个自动化测试实践中遇到的问题: 持续集成的自动化用例很多,测试环境日志level为debug,日志量大概40G/每天,定位问题时日志查询很慢,该怎么解决? 这个问题可…...
微信怎么开小店?【企业商家微信开店】
企业商家入局微信做营销已经是经营规划中必须做的一件事了,对于企业商家来说,最简单直接的方式就是开一个微信小店,然后通过自己宣传推广来在微信小店中成商品。那么企业商家在微信怎么开小店呢?下面内容分享给想在微信开店的企业…...
Java 中FastJson的使用【吃透FastJson】
如果不了解JSON格式,建议先看下:JSON数据格式【学习记录】 JSON序列化、反序列化JavaBean的框架有很多,最常见的Jackson、阿里巴巴开源的FastJson、谷歌的GSON、apache提供的json-lib等,下面我们主要来熟悉一下:Java语…...
Redis5.0集群搭建
Redis集群教程 此文重在介绍 Redis5.0 三主三从集群安装,无复杂难懂的概念,若想深入了解集群原理请参考Redis集群规范。 Redis集群介绍 Redis Cluster 提供一种 Redis 安装方式:数据自动在多个 Redis 节点间分片。 Redis Cluster 提供一定…...
继企业级信息系统开发学习1.1 —— Spring配置文件管理Bean
骑士救美计划采用构造方法注入属性值1、创建救美任务类2、创建救美骑士类2、创建救美骑士类3、创建旧救美骑士测试类3、配置救美骑士Bean5、创建新救美骑士测试类采用构造方法注入属性值 1、创建救美任务类 在net.huawei.spring.day01包里创建RescueDamselQuest类 Rescue Da…...
Web 容器、HTTP 服务器 、Servlet 容器区别与联系
首先浏览器发起 HTTP 请求,像早期的时候只会请求一些静态资源,这时候需要一个服务器来处理 HTTP 请求,并且将相应的静态资源返回。 这个服务器叫 HTTP 服务器。 简单点说就是解析请求,然后得知需要服务器上面哪个文件夹下哪个名字…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
Python实现简单音频数据压缩与解压算法
Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中,压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言,提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...
