前言
這是一包奧利奧(數(shù)組),里面藏了很多塊奧利奧餅干(數(shù)組中的元素),我將它們放在一個(gè)碟子上慢慢排好,從上往下一塊塊的拿起來(lái)(迭代),再一口氣吃掉,這就是今天的早餐,也就是要說(shuō)的 Iterator - 迭代器模式。
回顧
我們常用的 for 和 foreach,其實(shí)就是 MS 給我們封裝后的迭代器模式。為什么數(shù)組和集合能夠使用這兩個(gè)關(guān)鍵字呢?因?yàn)樗麄兌紝?shí)現(xiàn)了一個(gè)接口 IEnumerable,實(shí)現(xiàn)了內(nèi)部方法 GetEnumerator。我們對(duì)一個(gè)集合,或者是數(shù)組進(jìn)行遍歷的同時(shí),也就是數(shù)組或集合元素的下標(biāo)不斷遞增的一個(gè)過(guò)程。
圖
左邊的下標(biāo) 0 表示數(shù)組的第一個(gè)元素;
左邊的下標(biāo) 1 表示數(shù)組的第二個(gè)元素;
... ...
左邊的下標(biāo) i 表示數(shù)組的第i+1個(gè)元素;
最后一個(gè)元素就是數(shù)組的長(zhǎng)度 - 1;
UML 類圖
圖
圖
代碼分析
IEnumerable 接口
interface IEnumerable { IEnumerator GetEnumerator(); }
這里只有一個(gè)方法 GetEnumerator(),該方法可以生成一個(gè)遍歷集合的元素的迭代器。通過(guò)該迭代器,就可以進(jìn)行集合元素的遍歷了。
IEnumerator 接口
interface IEnumerator { bool MoveNext(); object GetCurrent(); }
實(shí)現(xiàn)該接口的實(shí)例可以成為迭代器。這里有兩個(gè)方法: MoveNext(),GetCurrent()。
MoveNext():移動(dòng)到下一個(gè)元素的下標(biāo),如果存在該下標(biāo)(沒(méi)有超出索引位置),則返回 true。主要用于終止循環(huán)條件。
GetCurrent():獲取當(dāng)前集合元素的值,不過(guò)這里因?yàn)榉祷氐念愋蜑?object,可能需要進(jìn)行強(qiáng)轉(zhuǎn),當(dāng)然,你也可以選擇使用泛型。
Dish.cs 類(碟子)
class Dish : IEnumerable { private readonly List<Aoliao> _aoliaos; public Dish() { _aoliaos = new List<Aoliao>(); } public IEnumerator GetEnumerator() { return new DishIterator(this); } public void AppendAoliao(Aoliao aoliao) { _aoliaos.Add(aoliao); } public int GetCount() { return _aoliaos.Count; } public Aoliao GetAoliao(int index) { if (index >= GetCount()) { throw new IndexOutOfRangeException(); } return _aoliaos[index]; } }
這是一個(gè)碟子類,因?yàn)樗鼘?shí)現(xiàn)了 IEnumerable 接口,我把它當(dāng)作集合,用于放置拆開(kāi)包裝后的奧利奧餅干。
這里的構(gòu)造函數(shù),進(jìn)行對(duì) List<Aoliao> 進(jìn)行集合的初始化。
AppendAoliao(Aoliao aoliao):在原有的集合中追加新元素,在放置好的奧利奧餅干后再添加一塊新的奧利奧餅干。
GetCount():獲取集合的個(gè)數(shù),獲取碟子上奧利奧餅干的總個(gè)數(shù)。
GetAoliao(int index):根據(jù)下標(biāo)獲取集合中的元素。
DishIterator.cs 類(碟子迭代器)
class DishIterator : IEnumerator { private int _index; private readonly Dish _dish; public DishIterator(Dish cookie) { _index = -1; _dish = cookie; } public bool MoveNext() { _index++; return _index < _dish.GetCount(); } public object GetCurrent() { try { return _dish.GetAoliao(_index); } catch (IndexOutOfRangeException) { throw new InvalidOperationException(); } } }
該類實(shí)現(xiàn)了 IEnumerator 接口,作為迭代器的一個(gè)實(shí)例對(duì)象,用于遍歷 Dish 對(duì)象內(nèi)集合的一個(gè)迭代器對(duì)象,這里有兩個(gè)字段:
_index:用于指定數(shù)組元素的下標(biāo),遞增,注意,這里我選擇讓下標(biāo)從 -1 開(kāi)始。
_dish:保存對(duì) Dish 類的一個(gè)引用。
MoveNext():移動(dòng)到下一個(gè)元素的下標(biāo),遞增下標(biāo) _index,假如索引超出界限則返回 false,從這里可以得知奧利奧餅干有沒(méi)有吃完。
GetCurrent():獲取當(dāng)前元素,根據(jù)下標(biāo) _index。
Aoliao.cs 類(奧利奧餅干)
class Aoliao { /// <summary> /// 味道 /// </summary> public bool Taste { get; set; } }
這里的 Taste 屬性,我只用于標(biāo)識(shí)它是否好吃。
Main.cs 類
class Program { static void Main(string[] args) { var dish = new Dish(); dish.AppendAoliao(new Aoliao() { Taste = true }); dish.AppendAoliao(new Aoliao() { Taste = true }); dish.AppendAoliao(new Aoliao() { Taste = true }); var iterator = dish.GetEnumerator(); while (iterator.MoveNext()) { var aoliao = (Aoliao)iterator.GetCurrent(); Console.WriteLine("味道: " + aoliao.Taste); } Console.Read(); } }
dish 作為一個(gè)數(shù)組,在一開(kāi)始初始化的時(shí)候放置幾塊奧利奧餅干,通過(guò) GetEnumerator() 可以得到迭代器,在 while 循環(huán)中,通過(guò) MoveNext() 可以移動(dòng)到集合的下一個(gè)元素下標(biāo),并取出奧利奧餅干,直到超出索引范圍(即奧利奧餅干已經(jīng)吃完)才會(huì)終止循環(huán)。這就是之前為什么我將 DishIterator 的下標(biāo)(_index)初始化值為 -1,MoveNext() 方法會(huì)先移動(dòng)光標(biāo)的位置,再?gòu)牡鞯?nbsp;GetCurrent() 方法取出當(dāng)前元素的值(根據(jù) MoveNext() 移動(dòng)后的下標(biāo))。
抽象的 UML 類圖
圖
圖
思考
【1】為什么要使用 Iterator 迭代器模式呢?對(duì)于集合,或者數(shù)組,我們直接使用 for 和 foreach 不就可以了嗎?
圖
觀察上述代碼,我們發(fā)現(xiàn)在 while 循環(huán)內(nèi)只涉及方法 MoveNext() 和 GetCurrent(),不依賴集合本身的 Dish 類對(duì)象,在遍歷時(shí)與集合沒(méi)有強(qiáng)耦合的關(guān)系,遍歷和實(shí)現(xiàn)進(jìn)行了分離。
也就是說(shuō),無(wú)論集合 Dish 本身如何變化,只要能夠正常返回 iterator 迭代器,我們就可以正常遍歷。
設(shè)計(jì)模式的作用就是幫助我們編寫可復(fù)用的類。所謂“可復(fù)用”,是指將類當(dāng)成“組件”,當(dāng)一個(gè)組件發(fā)生變化時(shí),會(huì)盡可能的減少對(duì)其他組件的影響,其他組件只需更少的修改或者不需要修改就可以繼續(xù)正常工作。
【2】為什么我們有 ConcreteEnumerable 和 ConcreteIterator 兩個(gè)具體類,還要額外創(chuàng)建一層接口呢?
我們總是幻想著使用實(shí)體類來(lái)解決遇到的所有問(wèn)題。如果只使用具體類來(lái)解決問(wèn)題,很容易增加類之間的強(qiáng)耦合度,這部分類也難以當(dāng)成組件多次利用。為了降低類之間的耦合度,為了增加類的利用度,從而引入了抽象類和接口。
【總結(jié)】優(yōu)先使用抽象類和接口來(lái)進(jìn)行編程,而不要總想著采用具體類來(lái)實(shí)現(xiàn)編程。
【博主】反骨仔
【原文】http://www.cnblogs.com/liqingwen/p/6550794.html
感謝您的閱讀。喜歡的、有用的就請(qǐng)大哥大嫂們賞幾個(gè)小錢花花,沒(méi)錢的就請(qǐng)高抬貴手“推薦一下”吧!你的物質(zhì)和精神支持是博主強(qiáng)大的寫作動(dòng)力。歡迎轉(zhuǎn)載!
博主的文章沒(méi)有高度、深度和廣度,只是湊字?jǐn)?shù)。由于博主的水平不高(其實(shí)是個(gè)菜B),不足和錯(cuò)誤之處在所難免,希望大家能夠批評(píng)指出。
博主是利用讀書(shū)、參考、引用、抄襲、復(fù)制和粘貼等多種方式打造成自己的純鍍 24k 文章,請(qǐng)?jiān)彶┲鞒蔀橐粋€(gè)無(wú)恥的文檔搬運(yùn)工!
http://www.cnblogs.com/liqingwen/p/6550794.html