.NET设计模式(18):迭代器模式(Iterator Pattern)(转)
概述
在面向對象的軟件設計中,我們經常會遇到一類集合對象,這類集合對象的內部結構可能有著各種各樣的實現,但是歸結起來,無非有兩點是需要我們去關心的:一是集合內部的數據存儲結構,二是遍歷集合內部的數據。面向對象設計原則中有一條是類的單一職責原則,所以我們要盡可能的去分解這些職責,用不同的類去承擔不同的職責。Iterator模式就是分離了集合對象的遍歷行為,抽象出一個迭代器類來負責,這樣既可以做到不暴露集合的內部結構,又可讓外部代碼透明的訪問集合內部的數據。
意圖
提供一種方法順序訪問一個聚合對象中各個元素, 而又不需暴露該對象的內部表示。[GOF 《設計模式》]
結構圖
Iterator模式結構圖如下:
圖1 ?Iterator模式結構圖
生活中的例子
迭代器提供一種方法順序訪問一個集合對象中各個元素,而又不需要暴露該對象的內部表示。在早期的電視機中,一個撥盤用來改變頻道。當改變頻道時,需要手工轉動撥盤移過每一個頻道,而不論這個頻道是否有信號。現在的電視機,使用[后一個]和[前一個]按鈕。當按下[后一個]按鈕時,將切換到下一個預置的頻道。想象一下在陌生的城市中的旅店中看電視。當改變頻道時,重要的不是幾頻道,而是節目內容。如果對一個頻道的節目不感興趣,那么可以換下一個頻道,而不需要知道它是幾頻道。
圖2 ?使用選頻器做例子的Iterator模式對象圖
Iterator模式解說
在面向對象的軟件設計中,我們經常會遇到一類集合對象,這類集合對象的內部結構可能有著各種各樣的實現,但是歸結起來,無非有兩點是需要我們去關心的:一是集合內部的數據存儲結構,二是遍歷集合內部的數據。面向對象設計原則中有一條是類的單一職責原則,所以我們要盡可能的去分解這些職責,用不同的類去承擔不同的職責。Iterator模式就是分離了集合對象的遍歷行為,抽象出一個迭代器類來負責,這樣既可以做到不暴露集合的內部結構,又可讓外部代碼透明的訪問集合內部的數據。下面看一個簡單的示意性例子,類結構圖如下:
圖3 示例代碼結構圖
首先有一個抽象的聚集,所謂的聚集就是就是數據的集合,可以循環去訪問它。它只有一個方法GetIterator()讓子類去實現,用來獲得一個迭代器對象。
/**////?<summary>///?抽象聚集
///?</summary>
public?interface?IList
{
????IIterator?GetIterator();
}
抽象的迭代器,它是用來訪問聚集的類,封裝了一些方法,用來把聚集中的數據按順序讀取出來。通常會有MoveNext()、CurrentItem()、Fisrt()、Next()等幾個方法讓子類去實現。
/**////?<summary>///?抽象迭代器
///?</summary>
public?interface?IIterator
{
????bool?MoveNext();
????Object?CurrentItem();
????void?First();
????void?Next();
}
具體的聚集,它實現了抽象聚集中的唯一的方法,同時在里面保存了一組數據,這里我們加上Length屬性和GetElement()方法是為了便于訪問聚集中的數據。
/**////?<summary>///?具體聚集
///?</summary>
public?class?ConcreteList?:?IList
{
????int[]?list;
????public?ConcreteList()
????{
????????list?=?new?int[]?{?1,2,3,4,5};
????}
????public?IIterator?GetIterator()
????{
????????return?new?ConcreteIterator(this);
????}
????public?int?Length
????{
????????get?{?return?list.Length;?}
????}
????public?int?GetElement(int?index)
????{
????????return?list[index];
????}
}
具體迭代器,實現了抽象迭代器中的四個方法,在它的構造函數中需要接受一個具體聚集類型的參數,在這里面我們可以根據實際的情況去編寫不同的迭代方式。
/**////?<summary>///?具體迭代器
///?</summary>
public?class?ConcreteIterator?:?IIterator
{
????private?ConcreteList?list;
????private?int?index;
????public?ConcreteIterator(ConcreteList?list)
????{
????????this.list?=?list;
????????index?=?0;
????}
????public?bool?MoveNext()
????{
????????if?(index?<?list.Length)
????????????return?true;
????????else
????????????return?false;
????}
????public?Object?CurrentItem()
????{
????????return?list.GetElement(index)?;
????}
????public?void?First()
????{
????????index?=?0;
????}
????public?void?Next()
????{
????????if?(index?<?list.Length)
????????{
????????????index++;
????????}
????}
}
簡單的客戶端程序調用:
/**////?<summary>///?客戶端程序
///?</summary>
class?Program
{
????static?void?Main(string[]?args)
????{
????????IIterator?iterator;
????????IList?list?=?new?ConcreteList();
????????iterator?=?list.GetIterator();
????????while?(iterator.MoveNext())
????????{
????????????int?i?=?(int)iterator.CurrentItem();
????????????Console.WriteLine(i.ToString());
????????????iterator.Next();
????????}
????????Console.Read();
????}
}
一個簡單的迭代器示例就結束了,這里我們并沒有利用任何的.NET特性,在C#中,實現Iterator模式已經不需要這么麻煩了,已經C#語言本身就有一些特定的實現,下面會說到。
.NET中的Iterator模式
在.NET下實現Iterator模式,對于聚集接口和迭代器接口已經存在了,其中IEnumerator扮演的就是迭代器的角色,它的實現如下:
public?interface?IEumerator{
????object?Current
????{
????????get;
????}
????bool?MoveNext();
????void?Reset();
}
屬性Current返回當前集合中的元素,Reset()方法恢復初始化指向的位置,MoveNext()方法返回值true表示迭代器成功前進到集合中的下一個元素,返回值false表示已經位于集合的末尾。能夠提供元素遍歷的集合對象,在.Net中都實現了IEnumerator接口。
IEnumerable則扮演的就是抽象聚集的角色,只有一個GetEnumerator()方法,如果集合對象需要具備跌代遍歷的功能,就必須實現該接口。
public?interface?IEnumerable{
????IEumerator?GetEnumerator();
}
下面看一個在.NET1.1下的迭代器例子,Person類是一個可枚舉的類。PersonsEnumerator類是一個枚舉器類。這個例子來自于http://www.theserverside.net/,被我簡單的改造了一下。
public?class?Persons?:?IEnumerable?{?
????public?string[]?m_Names;?
????public?Persons(params?string[]?Names)?
????{?
????????m_Names?=?new?string[Names.Length];?
????????Names.CopyTo(m_Names,0);?
????}?
????private?string?this[int?index]?
????{?
????????get?
????????{?
????????????return?m_Names[index];?
????????}?
????????set?
????????{?
????????????m_Names[index]?=?value;?
????????}?
????}
????public?IEnumerator?GetEnumerator()
????{
????????return?new?PersonsEnumerator(this);
????}
}
public?class?PersonsEnumerator?:?IEnumerator
{
????private?int?index?=?-1;
????private?Persons?P;
????public?PersonsEnumerator(Persons?P)
????{
????????this.P?=?P;
????}
????public?bool?MoveNext()
????{
????????index++;
????????return?index?<?P.m_Names.Length;
????}
????public?void?Reset()
????{
????????index?=?-1;
????}
????public?object?Current
????{
????????get
????????{
????????????return?P.m_Names[index];
????????}
????}
}?
來看客戶端代碼的調用:
class?Program?{?
????static?void?Main(string[]?args)?
????{?
????????Persons?arrPersons?=?new?Persons("Michel","Christine","Mathieu","Julien");?
????????foreach?(string?s?in?arrPersons)?
????????{?
????????????Console.WriteLine(s);?
????????}
????????Console.ReadLine();?
????}?
}
程序將輸出:
Michel?Christine?
Mathieu?
Julien
現在我們分析編譯器在執行foreach語句時到底做了什么,它執行的代碼大致如下:
class?Program?{?
????static?void?Main(string[]?args)?
????{?
??????????Persons?arrPersons?=?new?Persons("Michel","Christine","Mathieu","Julien");?
??????????IEnumerator?e?=?arrPersons.GetEnumerator();?
??????????while?(e.MoveNext())?
??????????{?
????????????Console.WriteLine((string)e.Current);?
??????????}
??????????Console.ReadLine();
????}?
}
可以看到這段代碼跟我們最前面提到的示例代碼非常的相似。同時在這個例子中,我們把大部分的精力都花在了實現迭代器和可迭代的類上面,在.NET2.0下面,由于有了yield return關鍵字,實現起來將更加的簡單優雅。下面我們把剛才的例子在2.0下重新實現一遍:
public?class?Persons?:?IEnumerable?{?
????string[]?m_Names;?
????public?Persons(params?string[]?Names)?
????{?
????????m_Names?=?new?string[Names.Length];?
????????Names.CopyTo(m_Names,0);?
????}?
????public?IEnumerator?GetEnumerator()?
????{?
????????foreach?(string?s?in?m_Names)?
????????{?
????????????yield?return?s;?
????????}?
????}?
}?
class?Program?
{?
????static?void?Main(string[]?args)?
????{?
????????Persons?arrPersons?=?new?Persons("Michel","Christine","Mathieu","Julien");?
????????foreach?(string?s?in?arrPersons)?
????????{?
????????????Console.WriteLine(s);?
????????}
????????Console.ReadLine();?
????}?
}
程序將輸出:
Michel?Christine?
Mathieu?
Julien
實現相同的功能,由于有了yield return關鍵字,變得非常的簡單。好了,關于.NET中的Iterator模式就說這么多了,更詳細的內容大家可以參考相關的資料。
效果及實現要點
1.迭代抽象:訪問一個聚合對象的內容而無需暴露它的內部表示。
2.迭代多態:為遍歷不同的集合結構提供一個統一的接口,從而支持同樣的算法在不同的集合結構上進行操作。
3.迭代器的健壯性考慮:遍歷的同時更改迭代器所在的集合結構,會導致問題。
適用性
1.訪問一個聚合對象的內容而無需暴露它的內部表示。
2.支持對聚合對象的多種遍歷。
3.為遍歷不同的聚合結構提供一個統一的接口(即, 支持多態迭代)。
總結
Iterator模式就是分離了集合對象的遍歷行為,抽象出一個迭代器類來負責,這樣既可以做到不暴露集合的內部結構,又可讓外部代碼透明的訪問集合內部的數據。
參考資料
Erich Gamma等,《設計模式:可復用面向對象軟件的基礎》,機械工業出版社
Robert C.Martin,《敏捷軟件開發:原則、模式與實踐》,清華大學出版社
閻宏,《Java與模式》,電子工業出版社
Alan Shalloway James R. Trott,《Design Patterns Explained》,中國電力出版社
MSDN WebCast 《C#面向對象設計模式縱橫談(18):Iterator 迭代器模式(行為型模式)》
總結
以上是生活随笔為你收集整理的.NET设计模式(18):迭代器模式(Iterator Pattern)(转)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何把Word里的公式放到PowerPo
- 下一篇: WCF消息拦截,利用消息拦截做身份验证服