一、模式概述
在面向對象設計時,我們常常需要辨認對象的職責。理想的狀态下,我們希望自己建立的對象隻具有一個職責。對象的責任越少,則該對象的穩定性就越好,受到的限制也就越少。職責分離,可以最大限度地減少彼此之間的耦合程度,進而建立一個松散耦合的對象網絡。
職責分離的要點是對被分離的職責進行封裝,并以抽象的方式建立起彼此之間的關系。在C#中,我們往往将這些可能變化的對象抽象為接口和抽象類,進而将原來的具體依賴改變為抽象依賴。對象不再受制于具體的實作細節,這就代表他們是可被替換的。
要在設計上做到這一點,首先就要學會分辨職責,學會分辨哪些職責是對象中可變的。以集合對象為例,集合是一個管理群組織資料對象的資料結構。這就表明集合首先應具備一個基本屬性,就是集合能夠存儲資料。這其中包含存儲資料的類型、存儲空間的大小、存儲空間的配置設定、以及存儲的方式和順序。不具備這些特點,則該對象就不成其為集合對象。也就是說,上述這些屬性是集合對象與身俱來的,是其密不可分的職責。然而,集合對象除了能夠存儲資料外,還必須提供通路其内部資料的行為方式,這是一種周遊機制。同時這種周遊方式,或會根據不同的情形提供不同的實作,如順序周遊,逆序周遊,或是二叉樹結構的中序、前序、後序周遊。
現在我們已經分辨出集合對象擁有的兩個職責:一是存儲内部資料;二是周遊内部資料。從依賴性來看,前者為集合對象的根本屬性,屬于一生俱生,一亡俱亡的關系;而後者既是可變化的,又是可分離的。是以,我們将周遊行為分離出來,抽象為一個疊代器,專門提供周遊集合内部資料對象的行為。這就是Iterator模式的本質。
如一個清單對象List,它提供了周遊清單各元素的能力,這種周遊的行為可能包含兩種:順序和逆序周遊。對于一般的List對象而言,采用順序周遊的方式;而對于特定的List對象,如ReverseList,則按照逆序周遊的方式通路其内部資料。如果不将存儲資料和通路資料的職責分離,為實作ReverseList類,就需要重寫其父類List中所有用于周遊資料的方法。現在我們采用Iterator模式,在List對象中分離出疊代器IListIterator,那就隻需要為這個繼承自List對象的ReverseList對象,提供逆序疊代器ReverseListIterator就可以了,如下面的類圖所示:

代碼如下:
public interface IListIterator
{
void First();
void Last();
bool MoveNext();
}
public class SequenceListIterator:IListIterator
private List list = null;
private int index;
public SequenceListIterator(List list)
{
index = -1;
this.list = list;
}
public void First()
index = 0;
public void Last()
index = list.Count;
public bool MoveNext()
index ++;
return list.Count > index;
public class ReverseListIterator:IListIterator
public ReverseListIterator(List list)
index = list.Count - 1;
index –;
return index >= 0;
public class List
public virtual IListIterator CreateIterator()
return new SequenceListIterator(this);
public class ReverseList:List
public override IListIterator CreateIterator()
return new ReverseListIterator(this);
我們看到,List類通過方法CreateIterator(),建立了SequenceListIterator對象,使得List集合對象能夠用順序疊代的方式周遊内部元素。而要使ReverseList采用逆序的方式周遊其内部元素,則隻需重寫父類List的CreateIterator()方法,通過建立ReverseListIterator對象,來建立集合與具體疊代器之間的關系。
二、.Net中的Iterator模式
在.Net中,IEnumerator接口就扮演了Iterator模式中疊代器的角色。IEnumerator的定義如下:
public interface IEnumerator
bool MoveNext();
Object Current {get; }
void Reset();
該接口提供了周遊集合元素的方法,其中主要的方法是MoveNext()。它将集合中的元素下标移到下一個元素。如果集合中沒有元素,或已經移到了最後一個,則傳回false。能夠提供元素周遊的集合對象,在.Net中都實作了IEnumerator接口。
我們在使用.Net集合對象時,會發現一個方法GetEnumerator()方法,它類似前面提到的List對象的CreateIterator()方法。該方法傳回的類型是IEnumerator,其内部則是建立并傳回一個具體的疊代器對象。正是通過這個方法,建立了具體的集合對象與疊代器之間的關系。
與通常的Iterator模式實作不同,.Net Framework将GetEnumerator()方法單獨抽象出來,定義了接口IEnumerable:
public interface IEnumerable
IEnumerator GetEnumerator();
IEnumerable接口就像疊代功能的辨別,如果集合對象需要具備疊代周遊的功能,就必須實作該接口,并在具體實作中,建立與自身有關系的具體疊代器對象。而要獲得該集合對象對應的疊代器,就可以通過該方法,如:
ArrayList al = new ArrayList();
IEnumerator iterator = al.GetEnumerator();
下面,我就以ArrayList對象為例,讨論一下.Net中Iterator模式的實作方式。首先,我們來看看ArrayList類的定義:
public class ArrayList : IList, ICloneable
它分别實作了IList和ICloneable接口。其中,ICloneable接口提供了Clone方法,與本文讨論的内容無關,略過不提。IList接口則提供了和連結清單相關的操作,如Add,Remove等。這也是ArrayList和Array的不同之處。而IList接口又實作了ICollection接口:
public interface IList : ICollection
ICollection接口是所有集合類型的公共接口,它提供了獲得集合長度和同步處理的一些方法,不過在這裡,我們需要注意的是它實作了IEnumerable接口:
public interface ICollection : IEnumerable
追本溯源,ArrayList類型間接地實作了IEnumerable接口。在ArrayList中,IEnumerable接口的GetEnumerator()方法實作代碼如下:
public virtual IEnumerator GetEnumerator()
return new ArrayListEnumeratorSimple(this);
GetEnumerator()方法是一個虛方法,這說明,我們可以自定義一個集合類型繼承ArrayList,重寫這個方法,建立和傳回不同的IEnumerator對象,進而實作不同的周遊方式。
為了實作ArrayList的周遊功能,采用的Iterator模式結構如下圖所示:
其中,類ArrayListEnumeratorSimple的實作如下所示:
[Serializable]
private class ArrayListEnumeratorSimple : IEnumerator, ICloneable
// Methods
internal ArrayListEnumeratorSimple(ArrayList list)
{
this.list = list;
this.index = -1;
this.version = list._version;
this.currentElement = list;
}
public object Clone(){//實作略}
public virtual bool MoveNext()
if (this.version != this.list._version)
{
throw new InvalidOperationException(Environment.GetResourceString(”InvalidOperation_EnumFailedVersion”));
}
if (this.index < (this.list.Count - 1))
this.index++;
this.currentElement = this.list[this.index];
return true;
this.currentElement = this.list;
this.index = this.list.Count;
return false;
public virtual void Reset()
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));
// Properties
public virtual object Current
get
object obj1 = this.currentElement;
if (obj1 != this.list)
{
return obj1;
}
if (this.index == -1)
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumNotStarted"));
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumEnded"));
// Fields
private object currentElement;
private int index;
private ArrayList list;
private int version;
ArrayListEnumeratorSimple實作了IEnumerator接口,實作了MoveNext()、Current、Reset()方法或屬性。該類是一個私有類型,其構造函數則被internal修飾符限制。在自定義的構造函數中,傳入的參數類型是ArrayList。正是通過構造函數傳遞需要周遊的ArrayList對象,來完成MoveNext()、Current、Reset()等操作。
下面,我們來看看如何通過IEnumerator來實作對ArrayList的周遊操作。
using System;
using System.Collections;
using NUnit.Framework
[TestFixture]
public class Tester
[Test]
public void TestArrayList()
ArrayList al = new ArrayList();
al.Add(5);
al.Add(“Test”);
IEnumerator e = al.GetEnumerator();
e.MoveNext();
Assert.AreEqual(5,e.Current);
Assert.AreEqual(“Test”,e.Current);
而要周遊ArrayList内部所有元素,方法也很簡單:
while (e.MoveNext())
Console.WriteLine(e.Current.ToString());
事實上,為了使用者更友善地周遊集合對象的所有元素,在C#中提供了foreach語句。該語句的實質正是通過IEnumerator的MoveNext()方法來完成周遊的。下面的語句與剛才那段代碼是等價的:
foreach (object o in al)
Console.WriteLine(o.ToString());
為了驗證foreach語句與疊代器的關系,我們來自定義一個ReverseArrayList類。要求周遊這個類的内部元素時,通路順序是逆序的。要定義這樣的一個類,很簡單,隻需要繼承ArrayList類,并重寫GetEnumerator()方法既可。
public class ReverseArrayList:ArrayList
public override IEnumerator GetEnumerator()
{
return new ReverseArrayListEnumerator(this);
}
其中,類ReverseArrayListEnumerator,實作了接口IEnumerator,它提供了逆序周遊的疊代器:
public class ReverseArrayListEnumerator:IEnumerator
public ReverseArrayListEnumerator(ArrayList list)
this.list = list;
this.index = list.Count;
this.currentElement = list;
#region IEnumerator Members
{
this.currentElement = this.list;
this.index = this.list.Count;
get
{
object obj1 = this.currentElement;
if (obj1 != this.list)
{
return obj1;
}
if (this.index == -1)
throw new InvalidOperationException("Out of the Collection");
throw new InvalidOperationException("Out of the Collection");
}
{
if (this.index > 0)
this.index–;
this.currentElement = this.list[this.index];
return true;
this.index = 0;
return false;
#endregion
注意ReverseArrayListEnumerator類與前面的ArrayListEnumeratorSimple類的差別,主要在于周遊下一個元素的順序。ReverseArrayListEnumerator中的MoveNext()方法,将下标往前移動,以保證元素周遊的逆序。同時在構造函數初始化時,将整個ArrayList對象的元素個數賦予下标的初始值:
this.index = list.Count;
我們來比較一下ArrayList和ReversieArrayList類之間,通過foreach周遊後的結果。
[STAThread]
public static void Main(string[] args)
ArrayList al = new ArrayList();
al.Add(1);
al.Add(2);
al.Add(3);
ReverseArrayList ral = new ReverseArrayList();
ral.Add(1);
ral.Add(2);
ral.Add(3);
Console.WriteLine(”The Sequence ArrayList:”);
foreach (int i in al)
Console.Write(”{0} “,i);
Console.WriteLine();
Console.WriteLine(”The Reverse ArrayList:”);
foreach (int i in ral)
Console.ReadLine();