我们用Unity的形式讲一下迭代器模式
你可以把迭代器模式想象成一个“导游”. 当你(客户端)去一个陌生的城市(聚合对象, 比如一个列表或集合)旅游时, 你不需要自己拿着地图到处找(直接访问集合内部), 而是雇一个导游(迭代器). 导游知道所有经典的位置和最佳路线, 它会带你一个一个地参观(遍历), 你只需要跟着他走, 不需要关心城市的具体布局.
举一个具体场景: 游戏中的背包系统
假设你正在开发一个RPG游戏, 玩家有一个背包(
Backpack), 里面存放了各种物品(Item). 你需要试下以下功能:- 遍历背包里的所有物品, 在UI上显示出来
- 遍历背包里的所有物品, 找到所有”武器”类型的物品
如果不使用迭代器模式, 你可能会直接在 UI 代码里访问背包的内部存储结构, 比如一个
List<Item> . 这回导致UI代码和背包的内部事件紧密耦合. 如果以后你想把背包的存储的方式从 List 改成 Dictionary 或自定义的 LinkedList , 你就必须修改所有遍历背包的UI代码, 非常麻烦迭代器模式如何解决这个问题?
迭代器模式通过引入一个”迭代器”(Iterator)接口, 将集合对象的”遍历行为”与集合对象本身分离开来.
- 背包(
Backpack)作为一个”聚合对象”, 它只负责存储物品, 并提供一个方法来创建一个”迭代器”.
- 迭代器(
BackpackIterator)负责定义遍历的接口(如HasNext()和Next()), 并实现具体的遍历逻辑
- UI管理器(
UIManager)作为”客户端”, 它只需要从背包获取一个迭代器, 然后使用这个迭代器来遍历物品, 完全不需要关心背包内部是用的什么数据结构存储的.
迭代器模式的核心角色
- 迭代器(
Iterator) - 是谁? 定义遍历集合元素的接口
- 作用是什么? 声明了用于遍历集合的方法, 主要是
HasNext()(是否还有下一个元素)和Next()(返回下一个元素). - 在我们的例子中:
ITemIterator接口.
- 具体迭代器(
Concrete Iterator) - 是谁? 实现迭代器接口的具体类
- 作用是什么? 实现了
HasNext()和Next()方法, 负责跟踪当前遍历的位置, 并与特定的聚合对象(如Backpack)进行交互来获取元素. - 在我们的例子中:
BackpackItertor类
- 聚合(
Aggregate) - 是谁? 定义创建相应迭代器对象的接口.
- 作用是什么? 它是一个代表元素集合的对象, 它知道如何创建一个迭代器来遍历自己.
- 在我们的例子中:
IItemAggregate接口.
- 具体组合(
Concrete Aggregate) - 是谁? 实现聚合接口的具体类, 即我们要遍历的实际集合.
- 作用是什么? 实现了创建迭代器的方法, 通常会返回一个与自身类型匹配的具体迭代器实例.
- 在我们的例子中:
Backpack类
代码实现步骤
- 定义物品类(
Item.cs)
这是一个简单的 POCO(Plain Old CLR Object)类, 代表背包里的物品.
- 定义迭代器接口(
IItemIterator.cs)
这个接口定义了遍历物品的统一方法
- 定义聚合接口(
IItemAggregate.cs)
这个接口定义了创建迭代器的方法.
- 实现具体聚合(
Backpack.cs)
这是我们的被堡垒, 它实现了
IItemAggregate接口, 并内部使用一个List<Item>来存储物品.- 实现具体迭代器(
BackpackIterator.cs)
这个迭代器负责遍历
Backpack 里的物品- 客户端使用(UIManager.cs)
UI 管理器作为客户端, 使用迭代器来遍历背包, 而不需要关心背包的内部实现
更”Unity 化”或”C# 化”的做法
在现代C#和Unity开发中, 我们很少会手动去实现
IItemIterator和IItemAggregate 这样的接口. 因为C# 已经内置了强大的 IEnumerable和 IENumerator接口, 以及 foreach循环, 它们本质上就是迭代器模式的一种语言级实现.上面的粒子如果用C#原生特性来写会更简洁:
虽然
foreach 隐藏了迭代器的创建和使用细节, 但它背后的原理正是迭代器模式.迭代器模式的核心优势
- 解耦遍历与集合: 集合对象(Backpack)和遍历行为(
BackpackIterator)被分离. 集合可以改变内部存储结构, 只要它仍然提供一个兼容的迭代器, 客户端代码(UIManager)就不需要做任何修改.
- 统一遍历接口: 无论集合的具体类型是什么(列表\字典\树等), 客户端都可以使用统一的
HasNext()/Next()或foreach当时遍历, 简化了客户端代码.
- 支持多种遍历方式: 你可以为同一个集合提供不同的迭代器, 以支持不同的遍历方式, 例如正序\倒序\或者根据特定调剂爱你过滤.
- 简化集合类: 集合类不再需要负责实现各种复杂的遍历逻辑, 使其职责更加单一.
在Unity中的其他应用场景
- UI列表: 遍历一个复杂的UI元素树, 查找特定类型的组件.
- AI行为: 一个AI可能有一个任务列表, 迭代器可以帮助它按优先级或其他顺序处理这些任务.
- 场景管理: 遍历场景中的所有敌人或特定类型的游戏对象.
总结: 迭代器模式提供了一种优雅的方式来访问集合对象的元素, 而无需暴露其内部表示. 它将”如何遍历”的责任从”存储什么”的责任中分离出来, 是构建灵活\可维护系统的重要工具. 在C#中, 我们通常通过
IEnumerable 和 foreach 来享受迭代器模式带来的便利.