C#[C# 设计情势] Iterator – 迭代器格局:作者与一份奥利奥早餐的逸事

Iterator – 迭代器情势

 

目录

  • 前言
  • 回顾
  • UML 类图
  • 代码分析
  • 抽象的 UML 类图
  • 思考

 

前言

  那是一包奥利奥(数组),里面藏了广大块奥利奥饼干(数组中的成分),作者将它们位于3个碟子上慢慢排好,从上往下一块块的拿起来(迭代),再一口气吃掉,那正是明日的早饭,也正是要说的 Iterator

  • 迭代器方式。

C# 1C# 2

 

 

回顾

  大家常用的 for 和 foreach,其实正是 MS
给我们封装后的迭代器情势。为啥数组和聚集能够选取那七个至关心珍视要字呢?因为他们都实现了1个接口
IEnumerable,完毕了中间方法 GetEnumerator。大家对二个集合,大概是数组进行遍历的同时,也正是数组或集合成分的下标不断递增的2个进度。

C# 3

  右侧的下标 0
表示数组的首先个因素;

  左边的下标 1
表示数组的第二个因素;

  … …

  左侧的下标 i
表示数组的第i+三个因素;

  最终三个元素正是数组的长短 –
1;

  

 UML 类图

C# 4

 图

C# 5

 

代码分析

   IEnumerable 接口

    interface IEnumerable
    {
        IEnumerator GetEnumerator();
    }

  那里唯有一个措施 GetEnumerator(),该形式可以生成贰个遍历集合的要素的迭代器。通过该迭代器,就足以开始展览集合成分的遍历了。

 

  IEnumerator 接口

    interface IEnumerator
    {
        bool MoveNext();

        object GetCurrent();
    }

  完成该接口的实例可以成为迭代器。那里有多个措施:
MoveNext(),GetCurrent()。

  MoveNext():移动到下一个成分的下标,假使存在该下标(没有当先索引地方),则赶回
true。首要用来终止循环条件。

  GetCurrent():获取当前集合成分的值,但是那里因为重临的种类为
object,恐怕要求开始展览强转,当然,你也得以采纳选拔泛型。

 

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

  这是1个碟子类,因为它完毕了 IEnumerable
接口,我把它看成集合,用于放置拆开包装后的奥利奥饼干。

  那里的构造函数,举行对 List<Aoliao> 实行联谊的初叶化。

  AppendAoliao(Aoliao
aoliao):在原本的集纳中加进新成分,在放置好的奥利奥饼干后再添加一块新的奥利奥饼干。

  GetCount():获取集合的个数,获取碟子上奥利奥饼干的总个数。

  GetAoliao(int index):依照下标获取集合中的成分。

 

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

  该类实现了 IEnumerator 接口,作为迭代器的二个实例对象,用于遍历 Dish
对象内汇集的三个迭代器对象,那里有四个字段:

  _index:用于钦定数组元素的下标,递增,注意,那里作者选择让下标从 -1
开首。

  _dish:保存对 Dish 类的1个引用。

  MoveNext():移动到下四个成分的下标,递增下标
_index,借使索引超出界限则赶回
false,从此间能够识破奥利奥饼干有没有吃完。

  GetCurrent():获取当前成分,依据下标 _index。

 

  Aoliao.cs 类(奥利奥饼干)

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

  那里的 Taste 属性,小编只用于标识它是或不是好吃。

  

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

C# 6

 

  dish
作为二个数组,在一从头先导化的时候放置几块奥利奥饼干,通过 GetEnumerator()
可以赢得迭代器,在 while 循环中,通过 MoveNext()
能够活动到集结的下2个因素下标,并取出奥利奥饼干,直到超出索引范围(即奥利奥饼干已经吃完)才会告一段落循环。那就是事先为何自身将 DishIterator
的下标(_index)开首化值为 -1,MoveNext()
方法会先活动光标的岗位,再从迭代器的 GetCurrent()
方法取出当前成分的值(依据 MoveNext() 移动后的下标)。

 

抽象的 UML 类图

C# 7

C# 8

 

思考

  【1】为何要选择 Iterator
迭代器模式吧?对于集合,只怕数组,大家平昔运用 for 和 foreach
不就足以了吧?

C# 9

  观看上述代码,大家发未来 while 循环内只涉及艺术 MoveNext() 和
GetCurrent(),不注重集合本身的 Dish
类对象,在遍历时与聚集没有强耦合的涉嫌,遍历和兑现举行了离别。

  也正是说,无论集合 Dish 本人如何转变,只要能够健康重返 iterator
迭代器,大家就能够正常遍历。

  设计方式的功力正是帮忙大家编辑可复用的类。所谓“可复用”,是指将类当成“组件”,当1个组件产生变化时,会尽恐怕的回落对其余零件的震慑,别的零件只需更少的修改或然不须求修改就足以持续健康办事。

  

  【2】为什么大家有 ConcreteEnumerable 和 ConcreteIterator
四个具体类,还要额外成立一层接口呢?

  大家连年幻想着使用实体类来化解碰着的保有毛病。倘诺只行使具体类来缓解难题,很不难扩大类之间的强耦合度,这部分类也麻烦当成组件数拾六次施用。为了下跌类之间的耦合度,为了扩大类的运开销,从而引入了抽象类和接口。

 

   【总括】优先利用抽象类和接口来进展编制程序,而并非总想着拔取具体类来贯彻编程。

 

 


【博主】反骨仔

【原文】http://www.cnblogs.com/liqingwen/p/6550794.html 

 

相关文章