设计模式——迭代器模式(Iterator Pattern)

概述

       迭代器模式(Iterator Pattern):提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标(Cursor)。迭代器模式是一种对象行为型模式。

       在软件开发中,我们经常需要使用聚合对象来存储一系列数据。聚合对象拥有两个职责:一是存储数据;二是遍历数据。从依赖性来看,前者是聚合对象的基本职责;而后者既是可变化的,又是可分离的。因此,可以将遍历数据的行为从聚合对象中分离出来,封装在一个被称之为“迭代器”的对象中,由迭代器来提供遍历聚合对象内部数据的行为,这将简化聚合对象的设计,更符合“单一职责原则”的要求。

       在迭代器模式结构中包含聚合和迭代器两个层次结构,考虑到系统的灵活性和可扩展性,在迭代器模式中应用了工厂方法模式,其模式结构如下图所示:

在迭代器模式结构图中包含如下几个角色:

       ● Iterator(抽象迭代器):它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法,例如:用于获取第一个元素的first()方法,用于访问下一个元素的next()方法,用于判断是否还有下一个元素的hasNext()方法,用于获取当前元素的currentItem()方法等,在具体迭代器中将实现这些方法。

       ● ConcreteIterator(具体迭代器):它实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置,在具体实现时,游标通常是一个表示位置的非负整数。

       ● Aggregate(抽象聚合类):它用于存储和管理元素对象,声明一个createIterator()方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。

       ● ConcreteAggregate(具体聚合类):它实现了在抽象聚合类中声明的createIterator()方法,该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例。

       在迭代器模式中,提供了一个外部的迭代器来对聚合对象进行访问和遍历,迭代器定义了一个访问该聚合元素的接口,并且可以跟踪当前遍历的元素,了解哪些元素已经遍历过而哪些没有。迭代器的引入,将使得对一个复杂聚合对象的操作变得简单。

       下面我们结合代码来对迭代器模式的结构进行进一步分析。在迭代器模式中应用了工厂方法模式,抽象迭代器对应于抽象产品角色,具体迭代器对应于具体产品角色,抽象聚合类对应于抽象工厂角色,具体聚合类对应于具体工厂角色。

简单实现
典型实现

在上图中,AbstractObjectList充当抽象聚合类,ProductList充当具体聚合类,AbstractIterator充当抽象迭代器,ProductIterator充当具体迭代器。完整代码如下所示:

编写客户端测试代码并运行:

       如果需要增加一个新的具体聚合类,如客户数据集合类,并且需要为客户数据集合类提供不同于商品数据集合类的正向遍历和逆向遍历操作,只需增加一个新的聚合子类和一个新的具体迭代器类即可,原有类库代码无须修改,符合“开闭原则”;如果需要为ProductList类更换一个迭代器,只需要增加一个新的具体迭代器类作为抽象迭代器类的子类,重新实现遍历方法,原有迭代器代码无须修改,也符合“开闭原则”;但是如果要在迭代器中增加新的方法,则需要修改抽象迭代器源代码,这将违背“开闭原则”。

内部类实现

       在迭代器模式结构图中,我们可以看到具体迭代器类和具体聚合类之间存在双重关系,其中一个关系为关联关系,在具体迭代器中需要维持一个对具体聚合对象的引用,该关联关系的目的是访问存储在聚合对象中的数据,以便迭代器能够对这些数据进行遍历操作。

       除了使用关联关系外,为了能够让迭代器可以访问到聚合对象中的数据,我们还可以将迭代器类设计为聚合类的内部类,JDK中的迭代器类就是通过这种方法来实现的,如下AbstractList类代码片段所示:

我们可以通过类似的方法来设计典型实现中的ProductList类,将ProductIterator类作为ProductList类的内部类,代码如下所示:

       无论使用哪种实现机制,客户端代码都是一样的,也就是说客户端无须关心具体迭代器对象的创建细节,只需通过调用工厂方法createIterator()即可得到一个可用的迭代器对象,这也是使用工厂方法模式的好处,通过工厂来封装对象的创建过程,简化了客户端的调用。

JDK内置迭代器

       为了让开发人员能够更加方便地操作聚合对象,在Java、C#等编程语言中都提供了内置迭代器。在Java集合框架中,常用的List和Set等聚合类都继承(或实现)了java.util.Collection接口,在Collection接口中声明了如下方法(部分):

       Collection接口中提供了一个iterator()方法,用于返回一个Iterator迭代器对象,以便遍历聚合中的元素;具体的Java聚合类可以通过实现该iterator()方法返回一个具体的Iterator对象。

       JDK中定义了抽象迭代器接口Iterator,代码如下所示:

       其中,hasNext()用于判断聚合对象中是否还存在下一个元素,为了不抛出异常,在每次调用next()之前需先调用hasNext(),如果有可供访问的元素,则返回true;next()方法用于将游标移至下一个元素,通过它可以逐个访问聚合中的元素,它返回游标所越过的那个元素的引用;remove()方法用于删除上次调用next()时所返回的元素。

       Java迭代器工作原理如下图所示,在第一个next()方法被调用时,迭代器游标由“元素1”与“元素2”之间移至“元素2”与“元素3”之间,跨越了“元素2”,因此next()方法将返回对“元素2”的引用;在第二个next()方法被调用时,迭代器由“元素2”与“元素3”之间移至“元素3”和“元素4”之间,next()方法将返回对“元素3”的引用,如果此时调用remove()方法,即可将“元素3”删除。

       但是iterator()方法源码中只提供了正向遍历,而有时候我们需要对一个聚合对象进行逆向遍历等操作,因此在JDK的ListIterator接口中声明了用于逆向遍历的hasPrevious()和previous()等方法,如果客户端需要调用这两个方法来实现逆向遍历,就不能再使用iterator()方法来创建迭代器了,正因为如此,在JDK的List接口中不得不增加对listIterator()方法的声明,该方法可以返回一个ListIterator类型的迭代器,ListIterator迭代器具有更加强大的功能。源码如下所示:

       在JDK中,Collection接口和Iterator接口充当了迭代器模式的抽象层,分别对应于抽象聚合类和抽象迭代器,而Collection接口的子类充当了具体聚合类。Java内置迭代器代码实现本文不再赘述。

总结

       迭代器模式是一种使用频率非常高的设计模式,通过引入迭代器可以将数据的遍历功能从聚合对象中分离出来,聚合对象只负责存储数据,而遍历数据由迭代器来完成。由于很多编程语言的类库都已经实现了迭代器模式,因此在实际开发中,我们只需要直接使用Java、C#等语言已定义好的迭代器即可,迭代器已经成为我们操作聚合对象的基本工具之一。

1. 主要优点

迭代器模式的主要优点如下:

       (1) 它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式。

       (2) 迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计。

       (3) 在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足“开闭原则”的要求。

2. 主要缺点

迭代器模式的主要缺点如下:

       (1) 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

       (2) 抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展,例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。

3. 适用场景

在以下情况下可以考虑使用迭代器模式:

       (1) 访问一个聚合对象的内容而无须暴露它的内部表示。将聚合对象的访问与内部数据的存储分离,使得访问聚合对象时无须了解其内部实现细节。

       (2) 需要为一个聚合对象提供多种遍历方式。

       (3) 为遍历不同的聚合结构提供一个统一的接口,在该接口的实现类中为不同的聚合结构提供不同的遍历方式,而客户端可以一致性地操作该接口。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UG装配-沿线运动

如果希望图中圆柱销沿着槽运动,直接约束面是困难的,我们可以画出圆弧的中心线和圆柱销的中心点,约束点在线上,进行移动 需要注意的是,我们在零件中画点和线的时候,在装配体默认加载模型引用集的时候是无法显…

最新ChatGPT网站源码,支持Midjourney绘画,GPT语音对话+GPT-4识图理解能力+ChatFile文档对话总结+DALL-E3文生图

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

基于 ESP32-C3 开启 Flash 加密和安全启动并进行 OTA 测试

软件: esp-idf v5.1.2 硬件: ESP32-C3 board 1. 首先,准备一个明文固件 hello-world.bin 基于 esp-idf-v5.1.2\examples\get-started\hello_world 例程,使用如下指令,直接编译,获取明文固件 hello-worl…

IDEA中自动导包及快捷键

导包设置及快捷键 设置:Setting->Editor->General->Auto import快捷键 设置:Setting->Editor->General->Auto import java区域有两个关键选项 Add unambiguous imports on the fly 快速添加明确的导包 IDEA将在我们书写代码的时候…

wpsjs学习——获取单元格批注

1.获取第一个单元格的值&#xff1a; wps.Application.ActiveSheet.Range(A1).Value2; 2.1.获取第一个单元格的批注&#xff1a; wps.Application.ActiveSheet.Range(A1).Comment.Text(); <div class"global">获取表格信息<div class"divItem">…

厚积薄发11年,鸿蒙究竟有多可怕

​12月20日中国工程院等权威单位发布《2023年全球十大工程成就》。本次发布的2023全球十大工程成就包括“鸿蒙操作系统”在内。入围的“全球十大工程成就”&#xff0c;主要指过去五年由世界各国工程科技工作者合作或单独完成且实践验证有效的&#xff0c;并且已经产生全球影响…

Zernike多项式法生成相位理论推导及图像引导实现原理

目录 引言 波前传感器 ​编辑 关于相位计算问题补充 关于结构图的修正 光束质量评价指标 Zernike多项式 ​编辑Zernike多项式法生成相位 光强分布求波前相位-GS 更快的迭代方法SPGD 基于Zernike模式的SPGD 引言 我们还是先从第一篇文献开始理解展开今天分享的一些重…

并查集(C++)

目录 一、并查集的原理二、并查集的实现路径压缩 三、并查集的应用结尾 一、并查集的原理 并查集的两个功能&#xff1a; 合并&#xff1a;合并两个不想联系的元素查询&#xff1a;判断两个元素是否在同一个组内 主要解决的是元素分组的问题。 例如&#xff1a;某班级要创建…

数据迁移怎么测,都有哪些步骤?

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;软件测试面试题分享&#xff1a; 1000道软件测试面试题及答案&#x1f4e2;软件测试实战项目分享&#xff1a; 纯接口项目-完…

gRPC - 分布式 gRPC 四种通信方式、三种代理方式(全代码演示)

目录 一、分布式 gRPC 开发 1.1、项目结构 & 前置说明 1.1.1、项目结构 1.1.2、protoc 必备依赖 1.1.3、推荐插件&#xff08;简化开发&#xff09; 1.1.4、protoc 生成 Java 代码说明 1.2、一元 RPC&#xff08;代理方式一&#xff1a;阻塞式 BlockingStub&#xff…

DFA算法在敏感词过滤的应用

相信大家对于游戏里聊天框的以下内容已经不陌生了 "我***"“你真牛*”“你是不是傻*” 一个垃圾的游戏环境是非常影响玩游戏的心情的&#xff0c;看到这些&#xff0c;就知道游戏已经帮我们屏蔽掉了那些屏蔽字了&#xff0c;对于玩游戏而言&#xff0c;心里会好受很…

D48|动态规划之编辑距离

583.两个字符串的删除操作 初始思路: 大概能想到定义dp数组为最少的删除次数 想不明白递归公式应该怎么推导 题解复盘&#xff1a; 第一种思路&#xff1a;dp[i][j]所需要删除元素的最少次数. 递归公式五部曲; 1)dp数组的定义&#xff1a; dp[i][j]&#xff1a;以i-1为结尾的…

力扣1944.队列中可以看到的人数--单调栈

思路&#xff1a; 由题知一个人能 看到 他右边另一个人的条件是这两人之间的所有人都比他们两人 矮 &#xff0c;也就是说&#xff0c;在自己右边第一个比自己高的人后面的人就肯定看不到了那么只需要找到右边第一个比自己高的人与自己之间的所有满足要求的人就行了&#xff0…

JDBC数据库访问——数据库操作

与指定的数据库建立连接后&#xff0c;就可以使用JDBC提供的API对数据库进行操作&#xff0c;包括查询、新增、更新、删除等。 1.查询操作 和数据库建立连接后&#xff0c;对数据库表进行查询操作的步骤如下&#xff1a; ①创建statement对象 由已创建的Connection对象con调…

透明OLED屏:种类与技术特点

作为一名专注于OLED技术研发的工程师&#xff0c;同时在尼伽工作多年&#xff0c;有幸能够参与到透明OLED屏的研发过程中。透明OLED屏作为一种新型显示技术&#xff0c;以其独特的透明特性和优秀的画质表现&#xff0c;正逐渐在各个领域崭露头角。在这篇文章中&#xff0c;我将…

GROUP_CONCAT报错解决

有如下表 其中awardee和awardee_unit都是保存的json类型的字符串, awardee是多个人员id, awardee_unit是部门的全路径 查询时要注意转换 需要将name拼接起来合并成一行,直接 GROUP_CONCAT 会报错 百度的大部分答案是修改数据库配置去掉严格模式,如果不方便修改数据库可以这样…

使用Go语言的HTTP客户端库进行API调用

随着微服务架构和RESTful API的普及&#xff0c;API调用成为了日常开发中的常见任务。Go语言提供了多种工具和库来帮助开发者轻松地与API进行交互。本文将介绍如何使用Go语言的HTTP客户端库进行API调用。 在Go语言中&#xff0c;标准库中的net/http包提供了基本的HTTP客户端功…

2023春季李宏毅机器学习笔记 06 :Diffusion Model 原理剖析

资料 课程主页&#xff1a;https://speech.ee.ntu.edu.tw/~hylee/ml/2023-spring.phpGithub&#xff1a;https://github.com/Fafa-DL/Lhy_Machine_LearningB站课程&#xff1a;https://space.bilibili.com/253734135/channel/collectiondetail?sid2014800 一、想法概念 Q1&…

基于哈里斯鹰算法优化的Elman神经网络数据预测 - 附代码

基于哈里斯鹰算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于哈里斯鹰算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于哈里斯鹰优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&…

C++学习day--25 俄罗斯方块游戏图像化开发

项目分析 项目演示、项目分析 启动页面 启动页面&#xff1a; 分析&#xff1a; 开发环境搭建 1&#xff09;安装vc2010, 或其他vs版本 2&#xff09;安装easyX图形库 代码实现: # include <stdio.h> # include <graphics.h> void welcome(void) { initgraph(55…