设计模式之迭代器模式

什么是迭代器模式
迭代器模式(Iterator pattern)是一种对象行为型设计模式,它提供了一种方法来顺序访问聚合对象中的元素,而又不暴露该对象的内部表示,同时也可以将迭代逻辑与聚合对象的实现分离,增强了代码的可维护性和可扩展性。
迭代器模式的特征如下:
- 迭代器模式允许一个聚合对象公开它的每一个元素,而又不暴露其内部表示,即它提供了一种方法来遍历聚合对象中的每一个元素,而不需要了解该对象的内部结构或实现。
- 迭代器模式的主要目的是将迭代逻辑封装在迭代器对象中,而不是将该逻辑嵌入到聚合对象中。这样可以将迭代逻辑与聚合对象的实现分离,使得它们可以独立地改变和扩展。
- 迭代器模式是通过定义一个迭代器接口来实现的。该接口提供了访问和遍历聚合对象的元素的方法。聚合对象则需要实现一个能够创建相应迭代器的接口。
迭代器模式的核心角色

- 迭代器接口(Iterator):定义了访问和遍历集合元素的方法,为使用者提供了一种方便的方式来遍历集合中的元素,而不必关心集合的实现细节,如:是否能够继续遍历下一个元素、取出下一个元素等。
- 具体迭代器(ConcreteIterator):持有一个具体的集合,并且实现了迭代器接口,实际负责按照特定的顺序遍历集合中的元素,并将遍历结果反馈给使用者。
- 集合接口(Aggregate):定义了创建迭代器的抽象方法,隐藏了具体集合的表示和实现,具体的表示和实现由具体集合类来负责实现;
- 具体集合(ConcreteAggregate):该类实现了集合接口,创建出具体的迭代器对象,供使用者通过迭代器来访问和遍历集合元素。当然,作为具体集合,需要提供具体的添加、移除、查询集合长度的具体方法;
迭代器模式如何实现
需求描述
回想在上大学的时候,老师在教学活动中都会拥有一个学生的花名册,上面有学生的姓名、学号等信息,每次上课前都要先按照名册上记录的顺序逐一点名,如果帮老师写一个自动点名的程序,那么这个时候使用迭代器模式绝对是非常不错的一个选择。那么具体怎么实现呢?
实现方法

1、定义一个迭代器接口,即名册迭代器接口,主要有两个抽象方法:判断是否下一个元素可以遍历、遍历取出下一个元素;
/*** 名删除迭代器抽象接口* @param <T>*/
public interface RosterInterator<T> {/*** 是否有下一个元素* @return*/boolean hasNext();/*** 取出下一个元素* @return*/T next();
}
2、定义一个集合接口:抽象名册接口,定义一个抽象方法:获取名册迭代器;
/*** 名册*/
public interface Roster<T> {/*** 获取迭代器* @return*/RosterInterator<T> getInterator();
}
3、定义具体的集合,即具体的学生名册类,具体的学生名册类除了正常可以往学生名册上添加、移除学生,查询学生名册上人员数量方法外,还要实现抽象名册接口的获取名册迭代器的方法,在方法中创建具体的迭代器对象;
/*** 学生类*/
@Data
public class Student {private String stuNo;private String name;public Student(String stuNo, String name) {this.stuNo = stuNo;this.name = name;}
}
/*** 学生名册*/
@Data
public class StudentRoster implements Roster<Student>{/*** 学生对象集合*/private List<Student> list=new ArrayList<>();/*** 添加学生* @param student*/public void add(Student student){this.list.add(student);}/*** 移除学生* @param student*/public void remove(Student student){this.list.remove(student);}/*** 学生名册学生对象数量* @return*/public Integer size(){return this.list.size();}@Overridepublic RosterInterator<Student> getInterator() {return new StudentRosterInterator(this);}
}
4、定义集合迭代器实现,即具体的学生名册迭代器,具体的学生迭代器会持有具体的学生名册,并实现名册迭代器定义的两个抽象方法,即具体的判断是否有下一个元素可以遍历、遍历取出下一个元素;
/*** 学生名册迭代器*/
@Data
public class StudentRosterInterator implements RosterInterator<Student>{/*** 学生名册*/private StudentRoster roster;/*** 索引位置*/private Integer index=0;public StudentRosterInterator(StudentRoster roster) {this.roster = roster;}@Overridepublic boolean hasNext() {return this.roster.size() > index;}@Overridepublic Student next() {Student student = this.roster.getList().get(index);index++;return student;}
}
5、编写客户端类
public class StudentClient {public static void main(String[] args) {StudentRoster studentRoster=new StudentRoster();Student stu1 = new Student("s001", "小明");Student stu2 = new Student("s002", "小红");Student stu3 = new Student("s003", "小刚");studentRoster.add(stu1);studentRoster.add(stu2);studentRoster.add(stu3);RosterInterator<Student> rosterInterator=new StudentRosterInterator(studentRoster);while (rosterInterator.hasNext()) {Student student = rosterInterator.next();System.out.println(student.getStuNo()+":"+student.getName());}}
}
如何扩展
老师上课的时候会根据学生画名册点名,那么学校领导在给老师们开会的时候,有没有可能也会点一个名,看年哪位老师没有到?当然会。假如也要帮校长实现一个对老师们的点名程序,应该怎么在原先的基础上扩展呢?其实很简单,保持原有的抽象名册迭代器接口、抽象名册接口不变,再分别实现老师画名册、老师画名册迭代器就可。而且这一过程完全符合开闭原则,不会对原来的程序造成任何影响,这就是设计模式的魅力。

/*** 老师类*/
@Data
public class Teacher {private String teacNo;private String name;public Teacher(String teacNo, String name) {this.teacNo = teacNo;this.name = name;}
}
/*** 老师名册*/
@Data
public class TeacherRoster implements Roster<Teacher>{/*** 老师对象集合*/private List<Teacher> list=new ArrayList<>();/*** 添加老师* @param teacher*/public void add(Teacher teacher){this.list.add(teacher);}/*** 移除老师* @param teacher*/public void remove(Teacher teacher){this.list.remove(teacher);}/*** 老师名册中对象数量* @return*/public Integer size(){return this.list.size();}@Overridepublic RosterInterator<Teacher> getInterator() {return new TeacherRosterInterator(this) ;}
}
/*** 老师名册迭代器*/
@Data
public class TeacherRosterInterator implements RosterInterator<Teacher> {private TeacherRoster teacherRoster;private Integer index = 0;public TeacherRosterInterator(TeacherRoster teacherRoster) {this.teacherRoster = teacherRoster;}@Overridepublic boolean hasNext() {return this.teacherRoster.size() > index;}@Overridepublic Teacher next() {Teacher teacher = this.teacherRoster.getList().get(index);index++;return teacher;}
}
public class TeacherClent {public static void main(String[] args) {TeacherRoster teacherRoster=new TeacherRoster();Teacher t1 = new Teacher("t001", "王老师");Teacher t2 = new Teacher("t002", "李老师");Teacher t3 = new Teacher("t003", "张老师");teacherRoster.add(t1);teacherRoster.add(t2);teacherRoster.add(t3);RosterInterator<Teacher> rosterInterator=new TeacherRosterInterator(teacherRoster);while (rosterInterator.hasNext()) {Teacher teacher = rosterInterator.next();System.out.println(teacher.getTeacNo()+":"+teacher.getName());}}
}
迭代器模式的适用场景
业务场景具有下面的特征就可以使用迭代器模式:
- 需要遍历集合对象中的元素,而不暴露该对象的内部表示的场景。通过使用迭代器模式,可以在不暴露集合对象内部结构的情况下,顺序访问集合对象中的各个元素。
- 需要为遍历不同的集合结构提供统一接口的场景。迭代器模式可以提供一种统一的接口,用于遍历不同的集合结构,从而使得代码更加灵活和可扩展。
有没有比较具体的业务场景示例呢?当然有,比如:
- 在物流系统中,可以使用迭代器模式来遍历物品。例如,在传送带上,不管传送的是什么物品,都被打包成一个一个的箱子并且有一个统一的二维码。这样我们不需要关心箱子里是什么物品,只需要一个一个检查发送的目的地即可。
- 在图片播放器中,可以使用迭代器模式来遍历图片。这样可以在不暴露图片内部结构的情况下,顺序访问图片中的各个元素。
- 图书馆管理系统:在图书馆中,需要对书籍进行遍历,可以使用迭代器模式来处理。通过定义一个统一的迭代器接口,不同的数据结构(如数组、链表等)都可以使用相同的迭代器接口来遍历书籍,同时保持内部实现的封装。
- 电商网站:在电商网站中,通常需要对商品进行展示和遍历。使用迭代器模式可以实现对商品的顺序访问,而不暴露商品内部表示。
- 金融系统:在金融系统中,需要对账户、交易等进行处理。使用迭代器模式可以实现对账户或交易的顺序访问,而不暴露其内部表示。
- 音乐播放器:在音乐播放器中,需要遍历音乐库中的歌曲。使用迭代器模式可以实现对歌曲的顺序访问,而不暴露歌曲内部表示。
总结
优点
- 分离集合与迭代逻辑:迭代器模式将集合对象与遍历逻辑分离,使得它们可以独立变化。集合对象只需要实现迭代器接口,而客户端只需要通过迭代器进行遍历操作,从而实现了解耦和模块化。
- 统一遍历接口:迭代器模式定义了一组统一的遍历接口,使得客户端可以以相同的方式对待不同类型的集合对象。无论是数组、链表、树状结构还是其他自定义集合,只要它们提供了符合迭代器接口的迭代器对象,就可以使用迭代器模式进行遍历,提高了代码的灵活性和可复用性。
- 简化客户端代码:使用迭代器模式可以简化客户端代码,减少了对集合内部结构的直接操作,只需要通过迭代器对象进行遍历操作。
缺点
- 可能增加代码复杂度:使用迭代器模式可能会增加一些额外的代码复杂度,例如需要定义迭代器接口、具体迭代器实现类等。
- 限制集合对象的类型:迭代器模式通常只适用于集合类型的聚合对象,不能很好地处理其他类型的聚合对象,例如树形结构、图形结构等。
- 可能增加内存开销:使用迭代器模式可能会增加一些额外的内存开销,例如需要创建迭代器对象等。
总之,迭代器模式在许多业务场景中都有应用,可以实现对集合对象的顺序访问,而不暴露其内部表示,可以使得代码更加清晰、简洁、易于维护。

相关文章:
设计模式之迭代器模式
什么是迭代器模式 迭代器模式(Iterator pattern)是一种对象行为型设计模式,它提供了一种方法来顺序访问聚合对象中的元素,而又不暴露该对象的内部表示,同时也可以将迭代逻辑与聚合对象的实现分离,增强了代码…...
使用SSH ,让windows和linux互通
简介 SSH 是一种安全网络协议,旨在让客户端和服务器之间进行安全的数据传输。SSH 的核心思想是利用公钥加密技术和共享密钥加密技术相结合的方式,使客户端和服务器之间建立起安全的连接。 当客户端发起连接请求时,服务器会对客户端进行身份验…...
常用设计模式——策略模式
策略模式是什么 策略模式(Strategy):针对一组算法,将每一个算法封装起来,从而使得它们可以相互替换。 比如我们一个软件的会员等级,每一个等级都会有对应的一些等级权益,那么每一个等级权益就…...
牛客网 CM11.链表分割
目录 1.解题思路2.代码实现 1.解题思路 此题目思路相对简单,利用双指针,一个指针指向小于val的,一个指针指向大于等于val的,但实现起来,如果仅仅使用单链表,那么还需特别判断第一个指针是否为空从而特意做…...
[iOS开发]iOS中TabBar中间按钮凸起的实现
在日常使用app的过程中,经常能看到人家实现了底部分栏控制器的中间按钮凸起的效果,那么这是怎么实现的呢? 效果演示: 实现原理: 创建按钮 创建一个UITabBar的子类,重写它的layoutSubviews方法࿱…...
数字时代,企业的数据共享意味着什么?
随着数字化整体在社会方方面面的推进,通过数据直接或间接创造的价值越来越大,逐渐成为了构建现代社会的重要要素。而对于企业来说,数据也是在数字经济中容易接触也切实能够利用产生大量价值,所以如何最大化利用数据,让…...
壹[1],QT自定义控件创建(QtDesigner)
1,环境 Qt 5.14.2 VS2022 原因:厌烦了控件提升的繁琐设置,且看不到界面预览显示。 2,QT制作自定义控件 2.1,New/其他项目/Qt4 设计师自定义控件 2.2,设置项目名称 2.3,设置 2.4,设…...
解决Java对接LDAP AD域登录出现Unprocessed Continuation Reference(s)错误
出现该错误的原因,主要是因为Java namingx的库,默认选项是未设置跟随,389返回的是AD域条目的引用,需要进行引用跟随。 解决方法分为两种,第一类不使用全局目录服务的端口389和636,而是使用真实端口 把代码…...
could not read ok from ADB Server
执行adb devices提示 List of devices attached * daemon not running; starting now at tcp:5037 could not read ok from ADB Server * failed to start daemon 方法1,关闭防火墙, could not read ok from ADB Server_夜星辰2023的博客-CSDN博客 我…...
超越基础:Flutter 中 onTap 的 5 条规则让你脱颖而出
小事情决定了你的熟练程度,这些小细节的有趣之处在于它们的丰富性。您将在代码库中的数百个位置遇到 onTap 事件。增强它们可以对代码的可维护性和最终用户体验产生重大的积极影响。 onTap 就是这样一个微小但丰富的东西——我们在每个屏幕上都使用它。这纯粹是关于…...
综合布线可视化管理系统价值分析
传统综合布线管理,全部依靠手工登记,利用标签标示线缆,利用文档资料记录链路的连接和变更,高度依赖网络管理员的管理能力,维护效率低下。同时,网络接入故障和非法接入难以及时发现。在以往的文章中小编一直…...
【JavaSE】基础笔记 - 类和对象(上)
目录 1、面向对象的初步认知 1.1、什么是面向对象 1.2、面向对象与面向过程 2. 类定义和使用 2.1、简单认识类 2.2、类的定义格式 2.3、自定义类举例说明 2.3.1、定义一个狗类 2.3.2、定义一个学生类 3、类的实例化 3.1、什么是实例化 3.2、类和对象的说明 1、面向…...
浅谈开口互感器在越南美的工业云系统中的应用
摘 要:分析低压开口式电流互感器的原理,结合工程实例分析开口电流互感器在低压配电系统中,主要是改造项目中的应用及施工细节,为用户快速实现智能配电提供解决方案,该方案具有成本低、投资少、安装接线简便等优点&…...
docker的使用以及注意事项
ssh的登录 1.登录ssh ssh 用户名IP地址 2.生成密钥 ssh-keygen生成密钥,在.ssh文件夹中(如果没有自己生成一个) 如果密钥之前已经生成过,可能在配置git的时候,会报错:这个密钥已经使用过的报错 解决方法是:otherwise[…...
大数据之LibrA数据库系统告警处理(ALM-12027 主机PID使用率超过阈值)
告警解释 系统每30秒周期性检测PID使用率,并把实际PID使用率和阈值进行比较,PID使用率默认提供一个阈值。当检测到PID使用率超出阈值时产生该告警。 平滑次数为1,主机PID使用率小于或等于阈值时,告警恢复;平滑次数大…...
软考 系统架构设计师系列知识点之数字孪生体(3)
接前一篇文章:软考 系统架构设计师系列知识点之数字孪生体(2) 所属章节: 第11章. 未来信息综合技术 第5节. 数字孪生体技术概述 3. 数字孪生体的关键技术 建模、仿真和基于数据融合的数字线程是数字孪生体的三项核心技术。能够做…...
新闻稿的写作注意事项!纯干货
新闻稿是企业、机构、政府等组织向公众传递信息的重要途径之一,也是媒体获取新闻素材的主要来源。一篇优质的新聞稿不仅可以吸引读者的注意力,还可以提高组织的形象和声誉。因此写好新闻稿至关重要。下面伯乐网络传媒来给大家探讨一些新闻稿写作的注意事…...
Android开发知识学习——从Retrofit原理来看HTTP
文章目录 Retrofit 使用方法简介Retrofit 源码结构总结扔物线读源码的思路与方式 Retrofit 使用方法简介 导包 implementation com.squareup.retrofit2:retrofit:最新版本创建一个 interface 作为 Web Service 的请求集合,在里面用注解 (Annotation&…...
计算机毕设 基于大数据的抖音短视频数据分析与可视化 - python 大数据 可视化
文章目录 0 前言1 课题背景2 数据清洗3 数据可视化地区-用户观看时间分界线每周观看观看路径发布地点视频时长整体点赞、完播 4 进阶分析相关性分析留存率 5 深度分析客户价值判断 5 最后 0 前言 🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,…...
1.OpenResty系列之入门简介
OpenResty(也称为ngx_openresty)是一个基于Nginx的全功能Web应用服务器,它将Nginx与一组附加模块和Lua脚本语言集成在一起,以提供高性能的Web应用程序开发和扩展性。 Nginx是一个轻量级的、高性能的HTTP服务器和反向代理服务器&a…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
Python常用模块:time、os、shutil与flask初探
一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...

