设计模式第六讲:责任链模式和迭代器模式详解
一. 责任链模式
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 服务器。 简单点说就是解析请求,然后得知需要服务器上面哪个文件夹下哪个名字…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...

基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...