迭代器模式提供一种模式顺序访问一个集合对象中的各个元素,而又不暴露其内部的表示。
11.2迭代器模式UML类图:
11.3应用场景:
由于每个集合对象本身不一样,所以当我们对不同类型的集合进行遍历时,由于必须暴露其内部表示,所写的遍历语句(for/foreach)也是不能通用的。而如果我们对需要用到的集合创建迭代器,由于对迭代器的遍历语句是相同的,则我们可以实现遍历语句的通用。另外,拥有了迭代器也就意味着我们对集合的实现是完全不知道的,以后一旦我们需要改变集合的实现,例如从数组改成ArrayList,客户端的代码是不需要做任何变动的,客户甚至不知道所遍历的集合发生了改变。
11.4迭代器模式分析与实现(c#描述):
//我们使用自己定义的迭代器接口示例。
namespace CustomIterator
{
//我们自己定义的迭代器接口
public interface IIterator
{
bool HasNext();//判断是否已到集合尾。
object Next();//该方法既返回当前值,同时也将集合当前指针移动到下一条记录。
}
//整型数组迭代器,用于对整型数组的遍历,这里应该也可以利用范型创建范型数组迭代器。
public class IteratorForArray : IIterator
{
private int[] iarr_Array;
private int iint_Position = 0;
public IteratorForArray(int[] aarr_Array)
{
this.iarr_Array = aarr_Array;
}
public bool HasNext()
{
if (this.iint_Position >= this.iarr_Array.Length)
{
return false;
}
else return true;
}
public object Next()
{
return this.iarr_Array[this.iint_Position++];
}
}
//ArrayList迭代器,用于对ArrayList的遍历。
public class IteratorForArrayList : IIterator
{
private ArrayList ial_ArrayList;
private int iint_Position = 0;
public IteratorForArrayList(ArrayList aal_Array)
{
this.ial_ArrayList = aal_Array;
}
public bool HasNext()
{
if (this.iint_Position >= this.ial_ArrayList.Count)
{
return false;
}
else return true;
}
public object Next()
{
return this.ial_ArrayList[this.iint_Position++];
}
}
//集合接口:一旦某一个类实现了集合接口IAggregate(IEnumerable)就表明该类中必定有某一集合需要用到迭代器,进一步:即可以对该类进行遍历。
public interface IAggregate
{
IIterator CreateIterator();//创建一个迭代器。
}
//持有某一集合的具体实现类
public class ShopKeeperA : IAggregate
{
private int[] iarr_SalesAmount = new int[] { 1, 4, 2, 5 };//为了简单起见,我们将店长每天的销售记录写在这里。
public IIterator CreateIterator()
{
return new IteratorForArray(this.iarr_SalesAmount);//由于A店长的销售记录是存储在整型数组中,所以这里用整型数组迭代器。
}
}
public class ShopKeeperB : IAggregate
{
private ArrayList ial_SalesAmount = new ArrayList() { 2, 3, 2, 1 };
public IIterator CreateIterator()
{
return new IteratorForArrayList(this.ial_SalesAmount);//由于B店长的销售记录是存储在ArrayList中,所以这里用ArrayList迭代器。
}
}
//调用类
public class IIterator_Test
{
public static void Do()
{
//下面对A店长拥有的集合进行遍历,输出1,4,2,5
IAggregate iAggregate = new ShopKeeperA();//注意:我们并不是对ShopKeeperA进行遍历,而是对ShopKeeperA中的集合进行遍历。
IIterator iIterator = iAggregate.CreateIterator();//为ShopKeeperA创建了迭代器,因此可以利用迭代器对ShopKeeperA中的集合进行遍历了。
while (iIterator.HasNext())
{
int lint_Temp = (int)iIterator.Next();
Console.WriteLine(lint_Temp);
}
//下面对B店长拥有的集合进行遍历,输出2,3,2,1
iAggregate = new ShopKeeperB();
iIterator = iAggregate.CreateIterator();//为ShopKeeperB创建了迭代器,因此可以利用迭代器对ShopKeeperB中的集合进行遍历了。
while (iIterator.HasNext())
{
int lint_Temp = (int)iIterator.Next();
Console.WriteLine(lint_Temp);
}
//回到迭代器模式的定义:迭代器模式提供一种模式顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
//这里我们遍历了ShopKeeperA和ShopKeeperB集合中的元素,却不知道该集合是用什么表示的(即不知道是集合用数组还是用ArrayList存储)
//如果我们以后改变集合的存储方式,客户端的代码是不需要做任何变动的,不是吗?
}
}
}
//使用C#中现有的迭代器接口示例。
namespace IteratorInCSharp {
//上例中我们定义的IAggregate在C#中对应为IEnumerable
//上例中我们定义的IIterator在C#中对应为IEnumerator(迭代器接口)
public class ShopKeeperA : IEnumerable
{
private int[] iarr_SalesAmount = new int[] { 1, 4, 2, 5 };
public IEnumerator GetEnumerator()
{
return this.iarr_SalesAmount.GetEnumerator();//C#已经为我们定义了数组的迭代器,所以我们不需要自己写了,直接调用即可。
}
}
public class ShopKeeperB : IEnumerable
{
private ArrayList ial_SalesAmount = new ArrayList() { 2, 3, 2, 1 };
public IEnumerator GetEnumerator()
{
return this.ial_SalesAmount.GetEnumerator();//C#已经为我们定义了ArrayList的迭代器,所以我们不需要自己写了,直接调用即可。
}
}
//调用类
public class IIterator_Test
{
public static void Do()
{
//下面对A店长拥有的集合进行遍历,输出1,4,2,5
IEnumerable iEnumerable = new ShopKeeperA();//注意:我们并不是对ShopKeeperA进行遍历,而是对ShopKeeperA中的集合进行遍历。
IEnumerator iEnumerator = iEnumerable.GetEnumerator();//为ShopKeeperA创建了迭代器,因此可以利用迭代器对ShopKeeperA中的集合进行遍历了。
while (iEnumerator.MoveNext())
{
int lint_Temp = (int)iEnumerator.Current;
Console.WriteLine(lint_Temp);
}
//上面这段代码实际上就是foreach语句,foreach是上面代码的一种语法上的简写。
//下面对B店长拥有的集合进行遍历,输出2,3,2,1
iEnumerable = new ShopKeeperB();
iEnumerator = iEnumerable.GetEnumerator();//为ShopKeeperA创建了迭代器,因此可以利用迭代器对ShopKeeperA中的集合进行遍历了。
while (iEnumerator.MoveNext())
{
int lint_Temp = (int)iEnumerator.Current;
Console.WriteLine(lint_Temp);
}
iEnumerator.Reset();//我们将迭代器复位,则该集合又可重新遍历了,再次输出2,3,2,1
while (iEnumerator.MoveNext())
{
int lint_Temp = (int)iEnumerator.Current;
Console.WriteLine(lint_Temp);
}
//另外C#中的foreach语句就是对实现了IEnumerable接口的类进行遍历,既然这里我们ShopKeeperA/B实现了IEnumerable接口,那么我们就可以利用foreach语句对其进行遍历
foreach (int lint_Temp in iEnumerable)//这里foreach语句实现上就是调用类的迭代器对集合进行遍历,由于我们迭代器接口(IEnumerator)中并没有对集合元素进行更改的方法,这也就决定了foreach语句不可能对集合元素进行“写”操作。
{
Console.WriteLine(lint_Temp);
}
}
}
}
至此,迭代器模式完毕。
迭代器模式把元素之间遍历的任务交给了迭代器来完成,而不是集合对象。这不仅让集合的接口和实现变得更加简洁,也可以让聚合更加专注在它所应该专注的事情上,即:管理对象集合,而不去理会遍历的事情。这也就是迭代器模式为我们带来的一个重要设计原则:设计原则:一个类应该只有一个引起变化的原因。因为类的每个责任都有改变的的潜在区域。超过一个责任,意味着超过一个改变的区域。这个原则告诉我们:应该让每个类保持单一责任。这里如果我们让集合类(数组,ArrayList)一方面管理自身的集合对象,又要担负例如遍历的责任时,我们就相当于给了这个类2个变化的原因。如果集合改变的话,这个类也必须改变,如果这个集合的遍历方式改变的话,这个类还是要改变。所以我们应该让一个类应该只有一个引起变化的原因,而不是多个。
另外注意一下:迭代器只是对集合遍历进行遍历,它并不假设集合元素之间有任何顺序关系。例如我们甚至可以针对散列表写一个迭代器。