迭代器模式介绍
迭代器模式是一种行为设计模式,让你能在不暴露集合底层表现形式(列表、栈、树等)的情况下遍历集合中所有的元素。
迭代器模式满足了单一职责和开闭原则,外界的调用方也不需要知道任何一个不同的数据结构在使用上的遍历差异。
迭代器模式的主要思想是将集合的遍历行为抽取为单独的迭代器对象。
除实现自身算法外,迭代器还封装了遍历操作的所有细节,比如当前位置和末尾剩余元素的数量、同时还有提供一个获取集合元素的基本方法。客户端可不断调用该方法直到它不返回任何内容(已遍历所有元素)、所有迭代器必须实现相同的接口(只要有合适的迭代器,客户端代码就能兼容任何类型的集合或遍历算法)。
迭代器的结构
- 迭代器接口: 声明了遍历集合所需的操作。(获取下一个元素,获取当前位置和重新开始迭代等)
- 具体迭代者:实现遍历集合的一种特定算法。(迭代器必须跟踪自身遍历的进度)
- 集合接口:声明一个或多个方法来获取与集合兼容的迭代器。(返回方法的类型必须被声明为迭代器接口,因此具体集合可以返回各种不同种类的迭代器)
- 具体集合:会在客户端请求迭代器时返回一个特定的具体迭代器类。
- 客户端:通过集合和迭代器的接口与两者进行交互。这样一来客户端无需与具体类进行耦合,允许同一客户端代码使用各种不同的集合和迭代器。
应用场景
- 当集合背后为复杂的数据结构,且你希望对客户端隐藏其复杂性时。
- 想避免程序中重复的遍历代码
- 程序代码能够遍历不同的甚至是无法预知的数据结构时。
实现方式
1、声明迭代器接口
2、声明集合接口并描述一个获取迭代器的方法,其返回值必须是迭代器接口。
3、为希望使用迭代器遍历的集合实现具体的迭代器类。
4、在你的集合类中实现集合接口。
5、在客户端使用迭代器替代所有集合遍历代码。
Demo
在迭代器的帮助下,客户端可以用一个迭代器接口以相似的方式遍历不同集合中的元素。
迭代器模式的辨别
- 迭代器可以通过导航方法(next和previous)来轻松识别。
- 使用迭代器的客户端代码可能没有其所遍历的集合的直接访问权限(二者是完全分离的)。
下面罗列了一个对人员集合进行排序的迭代器模式。
人员迭代器
/// <summary> /// 人员迭代器 /// 继承接口IEnumerator,显示实现了Current、MoveNext、Reset方法。 /// </summary> abstract class PeopleIterator:IEnumerator { public abstract int Key(); public abstract object Current(); public abstract bool MoveNext(); public abstract void Reset(); object IEnumerator.Current { get { return Current(); } } }
抽象集合迭代器和其实现
/// <summary> /// 抽象的集合迭代器 /// </summary> public abstract class AggregateIterator :IEnumerable { /// <summary> /// 得到内部的集合 /// </summary> /// <returns></returns> public abstract IEnumerator GetEnumerator(); } /// <summary> /// 集合的迭代器类 /// 具体排序的逻辑在这里写着 /// </summary> class AlphabeticalOrderIterator : PeopleIterator { private PeopleCollection _collection; private int _position = -1; private bool _reverse = false; public AlphabeticalOrderIterator(PeopleCollection collection,bool reverse=false) { this._collection = collection; _reverse = reverse; if (reverse) { _position = collection.getItems().Count; } } /// <summary> /// 当前的值 /// </summary> /// <returns></returns> public override object Current() { return this._collection.getItems()[_position]; } /// <summary> /// 此时的索引 /// </summary> /// <returns></returns> public override int Key() { return this._position; } /// <summary> /// 下一个 位置position进行调整 /// </summary> /// <returns></returns> public override bool MoveNext() { int updatedPosition = this._position + (this._reverse ? -1 : 1); if (updatedPosition >= 0 && updatedPosition < this._collection.getItems().Count) { this._position = updatedPosition; return true; } else { return false; } } /// <summary> /// 重置 /// </summary> public override void Reset() { this._position = this._reverse ? this._collection.getItems().Count - 1 : 0; } }
具体的人员集合和其Main()方法验证
/// <summary> /// 具体的人员集合 /// </summary> public class PeopleCollection :AggregateIterator { List<string> _collection = new List<string>(); bool _direction = false; public void ReverseDirection() { _direction = !_direction; } public List<string> getItems() { return _collection; } public void AddItem(string item) { _collection.Add(item); } public override IEnumerator GetEnumerator() { return new AlphabeticalOrderIterator(this,_direction); } } class Program { static void Main(string[] args) { var collection = new PeopleCollection(); collection.AddItem("阿五"); collection.AddItem("阿六"); collection.AddItem("阿七"); Console.WriteLine("遍历下"); foreach (var item in collection) { Console.WriteLine(item); } Console.WriteLine("倒序"); collection.ReverseDirection(); foreach (var item in collection) { Console.WriteLine(item); } Console.ReadKey(); } }
输出结果
迭代器模式在理解上还是有一点难度的,因为平时在使用集合时各个语言都已经将集合的各种操作方法都已经罗列出来了,调用人员直观调用即可。所以我们在了解其设计模式思想时,还是要多进行实践和思考。
有些东西看的多了,做的多了,自然而然就明白了。