我们用Unity的形式讲一下迭代器模式
 
你可以把迭代器模式想象成一个“导游”. 当你(客户端)去一个陌生的城市(聚合对象, 比如一个列表或集合)旅游时, 你不需要自己拿着地图到处找(直接访问集合内部), 而是雇一个导游(迭代器). 导游知道所有经典的位置和最佳路线, 它会带你一个一个地参观(遍历), 你只需要跟着他走, 不需要关心城市的具体布局.
 

举一个具体场景: 游戏中的背包系统

假设你正在开发一个RPG游戏, 玩家有一个背包(Backpack), 里面存放了各种物品(Item). 你需要试下以下功能:
  1. 遍历背包里的所有物品, 在UI上显示出来
  1. 遍历背包里的所有物品, 找到所有”武器”类型的物品
 
如果不使用迭代器模式, 你可能会直接在 UI 代码里访问背包的内部存储结构, 比如一个 List<Item> . 这回导致UI代码和背包的内部事件紧密耦合. 如果以后你想把背包的存储的方式从 List 改成 Dictionary 或自定义的 LinkedList , 你就必须修改所有遍历背包的UI代码, 非常麻烦
 

迭代器模式如何解决这个问题?

迭代器模式通过引入一个”迭代器”(Iterator)接口, 将集合对象的”遍历行为”与集合对象本身分离开来.
  1. 背包(Backpack)作为一个”聚合对象”, 它只负责存储物品, 并提供一个方法来创建一个”迭代器”.
  1. 迭代器(BackpackIterator)负责定义遍历的接口(如 HasNext()Next()), 并实现具体的遍历逻辑
  1. UI管理器(UIManager)作为”客户端”, 它只需要从背包获取一个迭代器, 然后使用这个迭代器来遍历物品, 完全不需要关心背包内部是用的什么数据结构存储的.

迭代器模式的核心角色

  1. 迭代器(Iterator)
    1. 是谁? 定义遍历集合元素的接口
    2. 作用是什么? 声明了用于遍历集合的方法, 主要是 HasNext()(是否还有下一个元素)和Next()(返回下一个元素).
    3. 在我们的例子中: ITemIterator 接口.
  1. 具体迭代器(Concrete Iterator)
    1. 是谁? 实现迭代器接口的具体类
    2. 作用是什么? 实现了 HasNext()Next()方法, 负责跟踪当前遍历的位置, 并与特定的聚合对象(如 Backpack)进行交互来获取元素.
    3. 在我们的例子中: BackpackItertor
  1. 聚合(Aggregate)
    1. 是谁? 定义创建相应迭代器对象的接口.
    2. 作用是什么? 它是一个代表元素集合的对象, 它知道如何创建一个迭代器来遍历自己.
    3. 在我们的例子中: IItemAggregate接口.
  1. 具体组合(Concrete Aggregate)
    1. 是谁? 实现聚合接口的具体类, 即我们要遍历的实际集合.
    2. 作用是什么? 实现了创建迭代器的方法, 通常会返回一个与自身类型匹配的具体迭代器实例.
    3. 在我们的例子中: Backpack
 

代码实现步骤

  1. 定义物品类(Item.cs)
这是一个简单的 POCO(Plain Old CLR Object)类, 代表背包里的物品.
 
  1. 定义迭代器接口(IItemIterator.cs)
这个接口定义了遍历物品的统一方法
 
  1. 定义聚合接口(IItemAggregate.cs)
这个接口定义了创建迭代器的方法.
 
  1. 实现具体聚合(Backpack.cs)
这是我们的被堡垒, 它实现了IItemAggregate接口, 并内部使用一个List<Item>来存储物品.
 
  1. 实现具体迭代器(BackpackIterator.cs)
这个迭代器负责遍历 Backpack 里的物品
 
  1. 客户端使用(UIManager.cs)
UI 管理器作为客户端, 使用迭代器来遍历背包, 而不需要关心背包的内部实现
 

更”Unity 化”或”C# 化”的做法

在现代C#和Unity开发中, 我们很少会手动去实现 IItemIteratorIItemAggregate 这样的接口. 因为C# 已经内置了强大的 IEnumerableIENumerator接口, 以及 foreach循环, 它们本质上就是迭代器模式的一种语言级实现.
 
上面的粒子如果用C#原生特性来写会更简洁:
虽然 foreach 隐藏了迭代器的创建和使用细节, 但它背后的原理正是迭代器模式.
 

迭代器模式的核心优势

  1. 解耦遍历与集合: 集合对象(Backpack)和遍历行为(BackpackIterator)被分离. 集合可以改变内部存储结构, 只要它仍然提供一个兼容的迭代器, 客户端代码(UIManager)就不需要做任何修改.
  1. 统一遍历接口: 无论集合的具体类型是什么(列表\字典\树等), 客户端都可以使用统一的 HasNext()/Next()foreach当时遍历, 简化了客户端代码.
  1. 支持多种遍历方式: 你可以为同一个集合提供不同的迭代器, 以支持不同的遍历方式, 例如正序\倒序\或者根据特定调剂爱你过滤.
  1. 简化集合类: 集合类不再需要负责实现各种复杂的遍历逻辑, 使其职责更加单一.
 
在Unity中的其他应用场景
  • UI列表: 遍历一个复杂的UI元素树, 查找特定类型的组件.
  • AI行为: 一个AI可能有一个任务列表, 迭代器可以帮助它按优先级或其他顺序处理这些任务.
  • 场景管理: 遍历场景中的所有敌人或特定类型的游戏对象.
 
总结: 迭代器模式提供了一种优雅的方式来访问集合对象的元素, 而无需暴露其内部表示. 它将”如何遍历”的责任从”存储什么”的责任中分离出来, 是构建灵活\可维护系统的重要工具. 在C#中, 我们通常通过 IEnumerableforeach 来享受迭代器模式带来的便利.