设计模式-迭代器模式

2023-11-15

迭代器模式

1、迭代器模式介绍

迭代器模式是一种行为型设计模式,它提供了一种方法来访问聚合对象中的各个元素,而不暴露其内部表示。通过使用迭代器,客户端可以遍历一个聚合对象中的元素,而不必了解其内部实现。

在迭代器模式中,定义了一个迭代器接口,该接口声明了能够访问聚合对象中元素的方法。然后,具体的迭代器实现类实现了这个接口,并提供了一个具体的遍历算法。聚合对象则负责创建并返回其对应的迭代器实例。

例如:在以前坐公交得自己交钱,售票员根据上车的人进行售票,不管什么人都要买票(比如小偷、公交车公司内部人员、程序员等同样要买票人人平等)。售票员其实就是将所有人都遍历了一遍,对每个乘客都要买票。

1.1 迭代器模式基本实现

迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。

迭代器模式结构图:

Aggregate聚集抽象类:

 

java

复制代码

/** * @author Shier * CreateTime 2023/5/13 21:07 * 聚集抽象类 */ public abstract class Aggregate { /** * 创建迭代器 * * @return */ public abstract Iterator createIterator(); }

ConcreteAggregate具体聚集类:继承Aggregate。

 

java

复制代码

/** * @author Shier * CreateTime 2023/5/13 21:09 * 具体聚集类 */ public class ConcreteAggregate extends Aggregate { /** * 存储聚合对象 */ private List<Object> list = new ArrayList<>(); @Override public Iterator createIterator() { return (Iterator) new ConcreteIterator(this); } /** * 返回聚合总个数 * * @return */ public int getCount() { return list.size(); } /** * 增加新对象 * * @param obj */ public void add(Object obj) { list.add(obj); } /** * 得到指定的对象 */ public Object getCurrentItem(int index) { return list.get(index); } }

Iterator迭代器抽象类:

 

java

复制代码

/** * @author Shier * CreateTime 2023/5/13 21:16 * Iterator 迭代器对象类 */ public abstract class Iterator { // 第一个 public abstract Object first(); // 下一个 public abstract Object netx(); // 是否到最后 public abstract boolean isDone(); // 当前对象 public abstract Object currentItems(); }

ConcreteIterator具体迭代器类:继承Iterator。

 

java

复制代码

/** * @author Shier * CreateTime 2023/5/13 21:15 */ public class ConcreteIterator extends Iterator { private ConcreteAggregate aggregate; public ConcreteIterator(ConcreteAggregate aggregate) { this.aggregate = aggregate; } private int current = 0; /** * 得到第一个对象 * * @return */ @Override public Object first() { return aggregate.getCurrentItem(0); } /** * 得到下一个对象 * * @return */ @Override public Object netx() { Object ret = null; current++; if (current < aggregate.getCount()) { ret = aggregate.getCurrentItem(current); } return ret; } /** * 判断是否到结尾 * * @return */ @Override public boolean isDone() { return current >= aggregate.getCount() ? true : false; } /** * 返回当前对象 * * @return */ @Override public Object currentItems() { return aggregate.getCurrentItem(current); } }

客户端代码:

 

java

复制代码

/** * @author Shier * CreateTime 2023/5/13 21:27 */ public class BaseClient { public static void main(String[] args) { // 聚集对象 上面的例子就相当于公交车bus ConcreteAggregate aggregateBus = new ConcreteAggregate(); aggregateBus.add("shier"); aggregateBus.add("公交公司员工"); aggregateBus.add("小菜"); aggregateBus.add("大白"); aggregateBus.add("小黑"); aggregateBus.add("小偷"); // 迭代器对象声明,即相当于售票员 ConcreteIterator conductor = new ConcreteIterator(aggregateBus); // 向第一个乘客售票 conductor.first(); while (!conductor.isDone()) { // 没有到最后一个则一直走向下一个乘客 System.out.println(conductor.currentItems() + ": 请买票!"); conductor.netx(); } } }

输出结果:

同时还可以实现倒序:

 

java

复制代码

/** * @author Shier * CreateTime 2023/5/13 21:15 * 具体迭代器类 - 倒序 */ public class ConcreteIteratorDesc extends Iterator { private ConcreteAggregate aggregate; private int current = 0; public ConcreteIteratorDesc(ConcreteAggregate aggregate) { this.aggregate = aggregate; // 从最后一个开始 current = aggregate.getCount() - 1; } /** * 得到倒数第一个对象 * * @return */ @Override public Object first() { return aggregate.getCurrentItem(aggregate.getCount() - 1); } /** * 得到下一个对象 * * @return */ @Override public Object netx() { Object ret = null; // 递减 current--; if (current < aggregate.getCount()) { ret = aggregate.getCurrentItem(current); } return ret; } /** * 判断是否到结尾 * * @return */ @Override public boolean isDone() { return current >= aggregate.getCount() ? true : false; } /** * 返回当前对象 * * @return */ @Override public Object currentItems() { return aggregate.getCurrentItem(current); } }

你想呀,售票员才不管你上来的是人还是物(行李),不管是中国人还 是外国人,不管是不是内部员工,甚至哪怕是马上要抓走的小偷,只要是来 乘车的乘客,就必须要买票。同样道理,当你需要访问一个聚集对象,而且 不管这些对象是什么都需要遍历的时候,你就应该考虑用迭代器模式。对聚集有多种方式遍历时,可以考虑用迭代器模式。售票员从车头到车尾来售票,也可以从车尾向车头来售票,也就是说, 你需要对聚集有多种方式遍历时,可以考虑用迭代器模式。由于不管乘客是 什么,售票员的做法始终是相同的,都是从第一个开始,下一个是谁,是否 结束,当前售到哪个人了,这些方法每天他都在做,也就是说,为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口

2、具体例子说明

2.1 购物车案例

上面也是举例一个坐公交车的列子

下面在用一个例子说明:

  1. 假设有一个超市的购物车,其中包含了多种商品,比如苹果、橙子、香蕉等。这个购物车可以看作是一个聚合对象,而其中每种商品则可以看作是购物车中的元素。

如果要遍历购物车中的所有商品,可以使用迭代器模式来实现。首先,定义一个迭代器接口,声明能够访问购物车中商品的方法。比如,可以定义一个Iterator接口,其中包含hasNext()next()两个方法。

  1. 然后,具体的迭代器实现类可以实现这个接口,并提供一个具体的遍历算法。例如,可以定义一个CartIterator类,它维护了购物车的内部状态,并实现了hasNext()next()方法,通过这两个方法来依次返回购物车中的每个商品。

  2. 最后,购物车可以负责创建并返回其对应的迭代器实例。可以定义一个Cart类,其中包含了购物车中所有商品的列表,以及一个getIterator()方法,用于返回一个CartIterator实例。

这样,客户端就可以通过调用getIterator()方法来获取购物车对应的迭代器,并使用它来遍历购物车中的所有商品。这种实现方式让遍历算法与聚合对象分离开来,使代码更加灵活和易于维护。

首先,定义迭代器接口 CartIterator

 

java

复制代码

public interface CartIterator { boolean hasNext(); Object next(); }

然后,定义具体的迭代器实现类 CartIteratorImpl

 

java

复制代码

public class CartIteratorImpl implements CartIterator { private List<Object> cartList; // 购物车中的商品列表 private int position; // 迭代器当前位置 public CartIteratorImpl(List<Object> cartList) { this.cartList = cartList; this.position = 0; } @Override public boolean hasNext() { return position < cartList.size(); } @Override public Object next() { if (!hasNext()) { throw new NoSuchElementException(); } Object item = cartList.get(position); position++; return item; } }

CartIteratorImpl 类中,我们使用了一个 List<Object> 来保存购物车中的商品列表,在 hasNext()next() 方法中维护了内部状态 position,以便顺序地遍历购物车中的每个商品。

最后,定义聚合对象 Cart,并在其中实现 getIterator() 方法:

 

java

复制代码

public class Cart { private List<Object> itemList; public Cart() { this.itemList = new ArrayList<>(); } public void addItem(Object item) { itemList.add(item); } public void removeItem(Object item) { itemList.remove(item); } public CartIterator getIterator() { return new CartIteratorImpl(itemList); } }

Cart 类中,我们使用了一个 List<Object> 来保存购物车中的商品列表,实现了添加和删除商品的方法。同时,实现了 getIterator() 方法,用于返回购物车对应的迭代器实例 CartIteratorImpl

最后,可以在客户端代码中使用 CartCartIterator 来遍历购物车中的所有商品:

 

java

复制代码

public class Client { public static void main(String[] args) { Cart cart = new Cart(); cart.addItem("苹果"); cart.addItem("橙子"); cart.addItem("香蕉"); CartIterator iterator = cart.getIterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } }

输出结果:

 

复制代码

苹果 橙子 香蕉

2.2 Java迭代器实现

起始在开发过程中不会向上面那样去搞,因为目前很多开发语言以及接入了这个迭代器模式,比如在Java中以及有了一个Iterator类

Java 中的 Iterator 类是迭代器模式的一种典型实现。它提供了一种统一的遍历方式,可以对不同类型的集合进行遍历,而无需关心集合内部的实现细节。

在 Java 中,每个实现了 Iterable 接口的对象都可以返回一个 Iterator 对象,用于遍历它所包含的元素。Iterator 接口定义了三个方法:

  • boolean hasNext():判断集合中是否还有下一个元素;
  • E next():返回集合中的下一个元素;
  • void remove():在迭代过程中移除集合中的当前元素,可选操作。

通过调用 hasNext()next() 方法,可以依次访问集合中的每个元素,直到遍历完所有元素为止。在遍历过程中,Iterator 对象负责维护内部状态,以便正确地返回下一个元素。

在迭代器模式中,迭代器对象将遍历算法和集合对象分离开来,避免了暴露集合内部实现细节,并将集合的遍历行为抽象为一个独立的接口。这使得代码更灵活、可扩展、易于维护。

比如,在 Java 中,可以使用 ArrayListLinkedListHashSet 等不同类型的集合类,并使用 Iterator 接口来实现对它们的遍历,而无需关心它们内部的实现细节。

3、迭代器模式总结

迭代器模式是一种行为型设计模式,它提供了一种访问集合对象内部元素的方式,而不用暴露集合的内部细节。

在迭代器模式中,集合对象和迭代器对象分别负责实现集合和遍历算法,并相互独立地进行演化和修改。这样可以避免暴露集合内部结构,也方便对集合进行扩展和修改。

迭代器模式主要由四个角色组成:

  • 抽象聚合类(Aggregate):定义集合对象的接口,包括添加、删除元素等方法。
  • 具体聚合类(ConcreteAggregate):实现抽象聚合类接口,存储集合中的元素。
  • 抽象迭代器类(Iterator):定义遍历集合的接口,包括获取下一个元素、判断是否还有下一个元素等方法。
  • 具体迭代器类(ConcreteIterator):实现抽象迭代器接口,负责对集合进行遍历操作。

迭代器模式的优点包括:

  • 将集合对象和遍历算法分离,使得代码更灵活、可扩展、易于维护。
  • 对客户端隐藏集合对象的内部实现,提高了代码的安全性。
  • 支持对同一种数据结构进行不同方式的遍历。

迭代器模式的缺点包括:

  • 需要实现迭代器对象和聚合对象,增加了代码复杂度。
  • 在集合内部元素发生变化时,需要及时更新迭代器状态,否则可能导致遍历结果不正确。

现在很多的开发语言都已经内置了迭代器模式,不要我们自己再去定义组件的迭代器(不能排除有特殊需求,还是得自己定义迭代器)

比如:

  1. 在 C# 中,集合类内部实现了 IEnumerableIEnumerator 接口,这两个接口就对应了迭代器模式中的抽象聚合类和抽象迭代器类。同时,C# 也提供了 yield 关键字,方便使用迭代器模式进行集合遍历。
  2. Python 中的 __iter__()__next__() 方法,Ruby 中的 each 方法等
  3. 在 Java 中,集合框架中的 Iterator 接口和相应的实现类就是迭代器模式的典型实现。同时,Java 8 还引入了基于 Lambda 表达式的 Stream API,它提供了非常便捷的集合遍历方式,并且可以进行各种数据处理和转换操作,进一步提高了代码的可读性、可维护性和可重用性。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式-迭代器模式 的相关文章

  • 如何在 Spring Data 中选择不同的结果

    我在使用简单的 Spring Data 查询或 Query 或 QueryDSL 在 Spring Data 中构建查询时遇到问题 如何选择三列 研究 国家 登录 不同的行 并且查询结果将是用户对象类型的列表 Table User Id S
  • 存根方法时出现 InvalidUseOfMatchersException

    我有这个 TestNG 测试方法代码 InjectMocks private FilmeService filmeService new FilmeServiceImpl Mock private FilmeDAO filmeDao Bef
  • Spring Security 自定义过滤器

    我想自定义 Spring security 3 0 5 并将登录 URL 更改为 login 而不是 j spring security check 我需要做的是允许登录 目录并保护 admin report html 页面 首先 我使用教
  • 如何在 Firebase 远程配置中从 JSON 获取值

    我是 Android 应用开发和 Firebase 的新手 我想知道如何获取存储在 Firebase 远程配置中的 JSONArray 文件中的值 String 和 Int 我使用 Firebase Remote Config 的最终目标是
  • 如何在 Antlr4 中为零参数函数编写语法

    我的函数具有参数语法 如下面的词法分析器和解析器 MyFunctionsLexer g4 lexer grammar MyFunctionsLexer FUNCTION FUNCTION NAME A Za z0 9 DOT COMMA L
  • 打印星号的 ASCII 菱形

    我的程序打印出这样的钻石 但只有当参数或菱形的每一面为4 例如如果我输入6 底部三角形的间距是错误的 我一直在试图找出答案 当参数改变时 底部的三角形不会改变 只有顶部的三角形会改变 它只适用于输入4 public static void
  • GWT - 如何组织项目以拥有多个网页以及它们之间的导航

    我是 GET 的新手 顺便说一句 它给我留下了深刻的印象 并且发现它对于像我这样熟悉 C NET 桌面技术并愿意编写 Web 应用程序的人来说非常有吸引力 我根据 GWT Eclipse 向导生成的示例启动了自己的项目 该项目生成带有面板的
  • 是否可以使用 Flying Saucer (XHTML-Renderer) 将 css 解析为类路径资源?

    我正在尝试将资源打包到 jar 中 但我无法让 Flying Saucer 在类路径上找到 css 我无法轻松构建 URL 来无缝解决此问题 https stackoverflow com questions 861500 url to l
  • 如何将 Mat (opencv) 转换为 INDArray (DL4J)?

    我希望任何人都可以帮助我解决这个任务 我正在处理一些图像分类并尝试将 OpenCv 3 2 0 和 DL4J 结合起来 我知道DL4J也包含Opencv 但我认为它没什么用 谁能帮我 如何转换成 INDArray 我尝试阅读一些问题here
  • 如何根据运行的 jar 的结果让我的 ant 任务通过或失败?

    我正在运行 CrossCheck 无浏览器 js 单元测试 作为 ant 脚本的一部分 如果 CrossCheck 测试失败 我希望 ant 报告失败 这是 build xml 中的相关部分
  • 当客户端关闭连接时,Spring StreamingResponseBody 请求线程未清理

    我在控制器中有一个端点 它返回一个StreamingResponseBody 用于向客户端发送文件 其代码大致如下 RestController RequestMapping value api public class Controlle
  • 套接字的读写如何同步?

    我们创建一个套接字 在套接字的一侧有一个 服务器 在另一侧有一个 客户端 服务器和客户端都可以向套接字写入和读取 这是我的理解 我不明白以下事情 如果服务器从套接字读取数据 它在套接字中是否只看到客户端写入套接字的内容 我的意思是 如果服务
  • 从 GitHub 上托管的 Spring Cloud Config Server 访问存储库的身份验证问题

    我在 GitHub 上的存储库中托管配置 如果我将回购公开 一切都好 但如果我将其设为私有 我将面临 org eclipse jgit errors TransportException https github com my user m
  • 使用架构注册表对 avro 消息进行 Spring 云合约测试

    我正在查看 spring 文档和 spring github 我可以看到一些非常基本的内容examples https github com spring cloud samples spring cloud contract sample
  • HashMap 值需要不可变吗?

    我知道 HashMap 中的键需要是不可变的 或者至少确保它们的哈希码 hashCode 不会改变或与另一个具有不同状态的对象发生冲突 但是 HashMap中存储的值是否需要与上面相同 为什么或者为什么不 这个想法是能够改变值 例如在其上调
  • “无法实例化活动”错误

    我的一个 Android 应用程序拥有大约 100 000 个用户 每周大约 10 次 我会通过 Google 的市场工具向我报告以下异常情况 java lang RuntimeException Unable to instantiate
  • 使用按钮作为列表的渲染器

    我想使用一个更复杂的渲染器 其中包含列表的多个组件 更准确地说 类似于this https stackoverflow com questions 10840498 java swing 1 6 textinput like firefox
  • 在浏览器刷新中刷新检票面板

    我正在开发一个付费角色系统 一旦用户刷新浏览器 我就需要刷新该页面中可用的统计信息 统计信息应该从数据库中获取并显示 但现在它不能正常工作 因为在页面刷新中 java代码不会被调用 而是使用以前的数据加载缓存的页面 我尝试添加以下代码来修复
  • 洪水填充优化:尝试使用队列

    我正在尝试创建一种填充方法 该方法采用用户指定的初始坐标 检查字符 然后根据需要更改它 这样做之后 它会检查相邻的方块并重复该过程 经过一番研究 我遇到了洪水填充算法并尝试了该算法 它可以工作 但无法满足我对 250 x 250 个字符的数
  • 在java中使用多个bufferedImage

    我正在 java 小程序中制作游戏 并且正在尝试优化我的代码以减少闪烁 我已经实现了双缓冲 因此我尝试使用另一个 BufferedImage 来存储不改变的游戏背景元素的图片 这是我的代码的相关部分 public class QuizApp

随机推荐

  • @ComponentScan中的basePackages

    这个注解一般都放在启动类上 作用 扫描包或者是类 如果扫描的是包 那么basePackages这个参数就可以省略 如果扫描的是类 那么这个参数就不可以省略 如果要加basePackages 格式如下 ComponentScan basePa
  • 远程连接服务器显示:发生身份验证错误 要求的函数不受支持

    问题不是出在服务器 而是出在远程桌面客户端 在微软打了补丁后 相当于限制了权限 修改要去登录服务器的那台客户端电脑就可以了 客户端操作系统 win7专业版 双击 加密Oracle修正 然后重新打开远程登录客户端就可以登录了
  • 【机器学习】5、模型融合与调优

    文章目录 一 模型选择 1 1 模型的选择 1 2 超参数的选择 二 模型效果优化 2 1 不同模型状态的处理 2 2 线性模型的权重分析 2 3 Bad case分析 2 4 模型融合 一 模型选择 1 1 模型的选择 确定场景 划分为模
  • FIO常用命令-自学(1)

    fio学习分享 1 根据fio Spec 第一部分为Command line options 以下几个参数比较常用 一 debug type 在debug或学习fio其他参数工作可以使用此命令 例debug io可以查看io的队列情况 也能
  • echartGL中option各3D配置属性配置详解

    最近 接手一个项目需要实现echart中各种3D图表样式 我还是一如既往的从研究echart配置项中各属性含义开始入手 由于自己需要实现的是柱状图效果 类似地图3D 效果在这里不展示 直接进入自己所研究的内容 1 grid3D 希望上面的图
  • vue - H5 移动端网页中调用手机摄像头扫描二维码 / 条形码功能,在h5手机网页里调起相机摄像头进行二维码或条形码扫描效果详细教程(详细示例组件源码,一键复制开箱即用!)

    效果图 实现了在vue网页项目中 实现调起手机摄像头进行扫描二维码或者条码 可自定义样式 直接复制组件代码 然后根据你的需求改一下 lt
  • jacobi迭代法_迭代法解线性方程组、Jaboci迭代法、Gauss–Seidel迭代法、松弛法

    适用方程组 系数矩阵为大型稀疏的方阵 引例 迭代法可以用来解方程 考虑解下面简单的方程 既然都说要用迭代法求解 就不能用正常的同除1 5求解 利用迭代法 把方程改写为以下形式 也可以看作为 你 与 的交点 画出交点的话更加清晰 设 带入方程
  • 商业框架AB包的 原理分析

    1 prefab 的单独 把所有依赖项全部打包到一个包里 加载prefab 的时候 其ab依赖的资源就在包里边 2 通用的资源在外打包到一个包里 打包时会遍历所有资源 然后把资源打包成一个个ab包 需要加载什么文件 就去加载 3 xml 中
  • 【程序人生】做了低薪运营6年,靠什么转行拿下 20W 年薪?

    本人大学专业非计算机相关 毕业六年一直从事运营相关工作 在上家公司的新业务系统项目中 因为项目组人手不足兼做了部分功能测试 让我对测试工作产生了浓厚的兴趣 后来 在xxx学习后 我从一个运营妹纸成功转型成为某世界500强公司的外包 年薪近2
  • MS17-010漏洞攻击与防御(利用永恒之蓝攻击Win7系统)

    任务1 利用永恒之蓝攻击Win7系统 在Kali终端中输入命令 msfconsole 启动Metasploit 输入命令 use auxiliary scanner smb smb ms17 010 加载扫描模块 输入命令 set RHOS
  • Boost电路硬件设计实例

    上一篇 Boost电路原理分析及其元件参数设计 Vane Zhang的博客 CSDN博客 本文以单相光伏发电系统中前级Boost电路为例对其进行硬件设计 Boost电路的硬件电路主要包括能量转换电路 开关管驱动电路和信号检测电路的设计 1
  • 半导体八大工艺流程图_大国重器,国芯基石 半导体离子注入机行业研究报告...

    主要观点 掺杂是指在硅晶体中加入少量的杂质元素 以此改变衬底材料的电学性质 是半导体加工制造过程中关键的工艺技术 根据掺杂的技术原理 该工艺可分为热扩散和离子注入 由于离子注入技术可以在芯片制成尺寸更小 空间结构更复杂的情况下实现元素掺杂
  • MCL2 -1.1.1

    大家好 承诺大家已久的1 1游戏体验优化 历经一个星期 也终于是完成了1 1 1版本 这个版本更新内容其实蛮多的 接下来就请大家拭目以待 更新目录 更新内容 更新代码 应用程序 更新内容 游戏封面体验增强 修复末影龙打完会闪退的bug 文件
  • C#企业微信 接收事件服务器(添加外部联系人事件)#openapi回调地址请求不通过# 完整源代码

    设置接收事件服务器 openapi回调地址请求不通过 企业微信api 添加外部联系人事件 using System Web UI WebControls using System IO using System Text using Sys
  • 从文本文件读取文件名,并删除指定路径下的文件

    void deleteFiles const QString path const QString delFileNameTxt const QString recycleBinPath QStringList delFileNames Q
  • 很多软件安装时为什么需要设置环境变量

    设置环境变量的目的 背景 在cmd中想要执行net start mysql等操作命令 必须先cd到bin文件所在目录 如D mysql mysql x x xx winx64 bin 那么每次打开mysql 都要输入那么多指令切换目录是不是
  • ME2M/ME3M增强 - ALV显示里增加字段(原)

    i s 本帖最后由 lulu1212 于 2012 10 12 11 23 编辑 之前用户提出要在ME2M ME3M里加字段 网上找了些资料发现是在结构里APPEND字段就可以了 结构名是 MEREP OUTTAB PURCHDOC 例如
  • RH850学习笔记

    这是一篇关于瑞萨RH850系列单片机的心得 网上关于这方面的资料特别少 可能是使用的人较少的原因吧 由于工作的原因 这段时间接触了这款单片机 所以写下了这篇博客 希望能给大家一些帮助 R7F701023 1023F1L是我最早接触的瑞萨MC
  • 算法笔记——动态规划

    算法笔记 动态规划 动态规划是一个非常灵活的算法 动态规划本身不难 无非就是一个状态转移的过程 难点就在于我们该如何去定义 状态 而这就需要我们多做题来积累经验 这也是初学者遇到动态规划往往无从下手的原因 动态规划的核心在于状态和状态转移方
  • 设计模式-迭代器模式

    迭代器模式 1 迭代器模式介绍 迭代器模式是一种行为型设计模式 它提供了一种方法来访问聚合对象中的各个元素 而不暴露其内部表示 通过使用迭代器 客户端可以遍历一个聚合对象中的元素 而不必了解其内部实现 在迭代器模式中 定义了一个迭代器接口