一、模式之前:
在谈iterator之前,先看一个OO的基本原则:单一职责原则(Single responsibility principle) 。
低耦合,高内聚(Low coupling,High cohesion)是软件工程强调的一个基本指导方针,也是为广大设计人员所追求的。而单一职责原则正是用于应对此问题的。单一职责强调每个类应只负责一项功能,以应对我们的老朋友—变化(Vary)。职责(功能)常常是变化的根源,一旦一个类涉及过多的职责(功能),这个类的‘把柄’也就相应增多了。
其实,这个原则也可以为我们在写方法时提供规范。很多良好编程习惯都建议一个方法不宜过长,一个方法应只做一件事,亦即最好做到‘原子化’。
我们经常会遇到一个类包含数据集合的情形,一般我们并不会担心类使用者如何遍历集合(遍历集合的职责),这完全得益于framework帮我们实现了一套iterator,iterator帮我们完成了遍历集合的职责。
二、模式之始:
迭代器模式和上面描述的情形又有点小差别,在前种情形下,集合一般实现为list等,使用方可以通过foreach来遍历元素。而迭代器模式会屏蔽集合的实现,即使用者不用知道集合的是list或者array等等。
标准的iterator模式大致如下图:

Iterator接口承担了遍历数据集合的职责(图中略去了具体的iterator)。HasNext判断是否含有更多元素,Next取出下一个元素,一般在使用时都有如下情形:
....
while( iter.HasNext() )
{
object item = iter.Next();
....
}
但如前面所说,framework已经包含了一套完整的机制供我们实现iterator模式:IEnumerable和IEnumerator以及其对应的泛型接口。
首先看看IEnumerable的代码:
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
很简单(类似于AbstractClass),再看下IEnumerator的实现:
public interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
}
这个接口包含了与上面Iterator接口功能相似的方法。因此在C#中实现迭代器,大可不必舍近求远:
using System;
using System.Collections;
public class Person
{
public Person(string fName, string lName)
{
this.firstName = fName;
this.lastName = lName;
}
public string firstName;
public string lastName;
}
public class People : IEnumerable
{
private Person[] _people;
public People(Person[] pArray)
{
_people = new Person[pArray.Length];
for (int i = 0; i < pArray.Length; i++)
{
_people[i] = pArray[i];
}
}
//实现IEnumerable
public IEnumerator GetEnumerator()
{
return new PeopleEnum(_people);
}
}
//将遍历的职责放入PeopleEnum
public class PeopleEnum : IEnumerator
{
public Person[] _people;
// Enumerators are positioned before the first element
// until the first MoveNext() call.
int position = -1;
public PeopleEnum(Person[] list)
{
_people = list;
}
public bool MoveNext()
{
position++;
return (position < _people.Length);
}
public void Reset()
{
position = -1;
}
public object Current
{
get
{
try
{
return _people[position];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
}
当我们需要访问People中的每一个Person时可以这样:
static void Main()
{
var peopleArray = new Person[3]
{
new Person("John", "Smith"),
new Person("Jim", "Johnson"),
new Person("Sue", "Rabon"),
};
var peopleList = new People(peopleArray);
var iter = peopleList.GetEnumerator();
while (iter.MoveNext())
{
var person = iter.Current as Person;
if (person != null)
{
Console.WriteLine(person.firstName + " " + person.lastName);
}
}
}
让人欣喜的是我们不用每次都while(...MoveNext()){...},在.Net下只要实现了IEnumerable(或其泛型版本),就可以使用foreach来进行遍历操作,这样一来,世界变得很干净了(使用泛型版本会更干净
):
static void Main()
{
var peopleArray = new Person[3]
{
new Person("John", "Smith"),
new Person("Jim", "Johnson"),
new Person("Sue", "Rabon"),
};
var peopleList = new People(peopleArray);
foreach (var obj in peopleList)
{
var person = obj as Person;
if( obj != null)
{
Console.WriteLine(person.firstName + " " + person.lastName);
}
}
}
上面的例子中出于说明模式的通用使用情形的目的,自定义了一个Enumerator,事实上,在framework中,大部分集合结构都已经实现了IEnumerble这个接口,因此很多时候我们是不用自己去实现这个接口的,而仅需要像下面这样:
public class People : IEnumerable
{
private object[] _people;
public People(Person[] pArray)
{
_people = new Person[pArray.Length];
for (int i = 0; i < pArray.Length; i++)
{
_people[i] = pArray[i];
}
}
//实现IEnumerable
public IEnumerator GetEnumerator()
{
return _people.GetEnumerator();
}
}
直接调用这些集合的GetEnumerator方法即可。
(实例代码来自MSDN,稍微做过一些修改)
三、模式之末:
两个关键点:
1.提供一种方法顺序访问一个聚合对象中各个元素
2.,不需暴露该对象的内部表示
以上为个人学习心得,请多指教。