前言

  這是一包奧利奧(數(shù)組),里面藏了很多塊奧利奧餅干(數(shù)組中的元素),我將它們放在一個(gè)碟子上慢慢排好,從上往下一塊塊的拿起來(lái)(迭代),再一口氣吃掉,這就是今天的早餐,也就是要說(shuō)的 Iterator - 迭代器模式。

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

 

 

回顧

  我們常用的 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ò)程。

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

  左邊的下標(biāo) 0 表示數(shù)組的第一個(gè)元素;

  左邊的下標(biāo) 1 表示數(shù)組的第二個(gè)元素;

  ... ...

  左邊的下標(biāo) i 表示數(shù)組的第i+1個(gè)元素;

  最后一個(gè)元素就是數(shù)組的長(zhǎng)度 - 1;

  

 UML 類圖

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

 圖

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

 

代碼分析

   IEnumerable 接口

    interface IEnumerable
    {
        IEnumerator GetEnumerator();
    }

  這里只有一個(gè)方法 GetEnumerator(),該方法可以生成一個(gè)遍歷集合的元素的迭代器。通過(guò)該迭代器,就可以進(jìn)行集合元素的遍歷了。

 

  IEnumerator 接口

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

    interface IEnumerator
    {        bool MoveNext();        object GetCurrent();
    }

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

  實(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 類(碟子)

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

    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];
        }
    }

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

  這是一個(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 類(碟子迭代器)

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

    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();
            }
        }
    }

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

  該類實(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 類(奧利奧餅干)

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

    class Aoliao
    {        /// <summary>
        /// 味道        /// </summary>
        public bool Taste { get; set; }
    }

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

  這里的 Taste 屬性,我只用于標(biāo)識(shí)它是否好吃。

  

  Main.cs 類

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

    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();
        }
    }

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

 

  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 類圖

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

 

思考

  【1】為什么要使用 Iterator 迭代器模式呢?對(duì)于集合,或者數(shù)組,我們直接使用 for 和 foreach 不就可以了嗎?

大學(xué)生就業(yè)培訓(xùn),高中生培訓(xùn),在職人員轉(zhuǎn)行培訓(xùn),企業(yè)團(tuán)訓(xùn)

  觀察上述代碼,我們發(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)指出。

  • 我的博客:http://www.cnblogs.com/liqingwen/

  • 博主是利用讀書(shū)、參考、引用、抄襲、復(fù)制和粘貼等多種方式打造成自己的純鍍 24k 文章,請(qǐng)?jiān)彶┲鞒蔀橐粋€(gè)無(wú)恥的文檔搬運(yùn)工!

http://www.cnblogs.com/liqingwen/p/6550794.html