当前位置:首页 > .Net > C#设计模式系列(16)-迭代器模式

C#设计模式系列(16)-迭代器模式

迭代器模式遍历集合的成熟模式,迭代器模式的关键是将遍历集合的任务交给一个叫做迭代器的对象,它的工作时遍历并选择序列中的对象,而客户端程序员不必知道或关心该集合序列底层的结构。

设计模式

一、引言

在上篇博文中分享了我对命令模式的理解,命令模式主要是把行为进行抽象成命令,使得请求者的行为和接受者的行为形成低耦合。在一章中,将介绍一下迭代器模式。下面废话不多说了,直接进入本博文的主题。

二、迭代器模式的介绍

迭代器是针对集合对象而生的,对于集合对象而言,必然涉及到集合元素的添加删除操作,同时也肯定支持遍历集合元素的操作,我们此时可以把遍历操作也放在集合对象中,但这样的话,集合对象就承担太多的责任了,面向对象设计原则中有一条是单一职责原则,所以我们要尽可能地分离这些职责,用不同的类去承担不同的职责。迭代器模式就是用迭代器类来承担遍历集合元素的职责。

2.1 迭代器模式的定义

迭代器模式提供了一种方法顺序访问一个聚合对象(理解为集合对象)中各个元素,而又无需暴露该对象的内部表示,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

2.2 迭代器模式的结构

既然,迭代器模式承担了遍历集合对象的职责,则该模式自然存在2个类,一个是聚合类,一个是迭代器类。在面向对象涉及原则中还有一条是针对接口编程,所以,在迭代器模式中,抽象了2个接口,一个是聚合接口,另一个是迭代器接口,这样迭代器模式中就四个角色了,具体的类图如下所示:

设计模式

从上图可以看出,迭代器模式由以下角色组成:

  • 迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口
  • 具体迭代器角色(Concrete Iteraror):具体迭代器角色实现了迭代器接口,并需要记录遍历中的当前位置。
  • 聚合角色(Aggregate):聚合角色负责定义获得迭代器角色的接口
  • 具体聚合角色(Concrete Aggregate):具体聚合角色实现聚合角色接口。
  • 2.3 迭代器模式的实现

    介绍完迭代器模式之后,下面就具体看看迭代器模式的实现,具体实现代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    
    // 抽象聚合类
        public interface IListCollection
        {
            Iterator GetIterator();
        }
     
     
    // 迭代器抽象类
        public interface Iterator
        {
            bool MoveNext();
            Object GetCurrent();
            void Next();
            void Reset();
        }
     
     
    // 具体聚合类
        public class ConcreteList : IListCollection
        {
            int[] collection;
            public ConcreteList()
            {
                collection = new int[] { 2, 4, 6, 8 };
            }
     
            public Iterator GetIterator()
            {
                return new ConcreteIterator(this);
            }
     
            public int Length
            {
                get { return collection.Length; }
            }
     
            public int GetElement(int index)
            {
                return collection[index];
            }
        }
     
     
    // 具体迭代器类
        public class ConcreteIterator : Iterator
        {
     
    // 迭代器要集合对象进行遍历操作,自然就需要引用集合对象
            private ConcreteList _list;
            private int _index;
     
            public ConcreteIterator(ConcreteList list)
            {
                _list = list;
                _index = 0;
            }
     
            public bool MoveNext()
            {
                if (_index < _list.Length)
                {
                    return true;
                }
                return false;
            }
     
            public Object GetCurrent()
            {
                return _list.GetElement(_index);
            }
     
            public void Reset()
            {
                _index = 0;
            }
     
            public void Next()
            {
                if (_index < _list.Length)
                {
                    _index++;
                }
     
            }
        }
     
     
    // 客户端
        class Program
        {
            static void Main(string[] args)
            {
                Iterator iterator;
                IListCollection list = new ConcreteList();
                iterator = list.GetIterator();
     
                while (iterator.MoveNext())
                {
                    int i = (int)iterator.GetCurrent();
                    Console.WriteLine(i.ToString());
                    iterator.Next();
                }
     
                Console.Read();
            }
        }

    自然,上面代码的运行结果也是对集合每个元素的输出,具体运行结果如下图所示:

    设计模式

    三、.NET中迭代器模式的应用

    在.NET下,迭代器模式中的聚集接口和迭代器接口都已经存在了,其中IEnumerator接口扮演的就是迭代器角色,IEnumberable接口则扮演的就是抽象聚集的角色,只有一个GetEnumerator()方法,关于这两个接口的定义可以自行参考MSDN。在.NET 1.0中,.NET 类库中很多集合都已经实现了迭代器模式,大家可以用反编译工具Reflector来查看下mscorlib程序集下的System.Collections命名空间下的类,这里给出ArrayList的定义代码,具体实现代码可以自行用反编译工具查看,具体代码如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    public class ArrayList : IList, ICollection, IEnumerable, ICloneable
    {
     
    // Fields
        private const int _defaultCapacity = 4;
        private object[] _items;
        private int _size;
        [NonSerialized]
        private object _syncRoot;
        private int _version;
        private static readonly object[] emptyArray;
     
        public virtual IEnumerator GetEnumerator();
        public virtual IEnumerator GetEnumerator(int index, int count);
     
     
    // Properties
        public virtual int Capacity { get; set; }
        public virtual int Count { get; }
        ..............
    // 更多代码请自行用反编译工具Reflector查看
    }

    通过查看源码你可以发现,ArrayList中迭代器的实现与我们前面给出的示例代码非常相似。然而,在.NET 2.0中,由于有了yield return关键字,实现迭代器模式就更简单了,关于迭代器的更多内容可以参考我的这篇博文。

    四、迭代器模式的适用场景

    在下面的情况下可以考虑使用迭代器模式:

    • 系统需要访问一个聚合对象的内容而无需暴露它的内部表示。
    • 系统需要支持对聚合对象的多种遍历。
    • 系统需要为不同的聚合结构提供一个统一的接口。

    设计模式

    五、迭代器模式的优缺点

    由于迭代器承担了遍历集合的职责,从而有以下的优点:

    • 迭代器模式使得访问一个聚合对象的内容而无需暴露它的内部表示,即迭代抽象。
    • 迭代器模式为遍历不同的集合结构提供了一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作

    迭代器模式存在的缺陷:

    • 迭代器模式在遍历的同时更改迭代器所在的集合结构会导致出现异常。所以使用foreach语句只能在对集合进行遍历,不能在遍历的同时更改集合中的元素。

    六、总结

    什么时候用它呢?

    • 访问一个聚合对象的内容而无需暴露它的内部表示
    • 支持对聚合对象的多种遍历
    • 为遍历不同的聚合结构提供一个统一的接口
  • << C#设计模式系列(15)-命令模式
  • C#设计模式系列(17)-观察者模式 >>
  • 作者:
    除非注明,本文原创:知道91,欢迎转载!转载请以链接形式注明本文地址,谢谢。
    原文链接:http://www.zhidao91.com/csharp-list16/

    相关文章 近期热评 最新文章

    • oracle数据库相关操作注意事项
      修改Oracle SGA(共享内存) 很多网站说修改Oracle的内存通过命令 如果你这么做了,那么恭喜你,你的Oracle数据库无法启动了。如果你已经这么做了,恢复Oracle启动的方...
    • 使用微信JDK实现微信接口签名验证
      要使用微信的接口必须在绑定的域名下测试;签名必须先向微信请求到access_token,然后用access_token再去请求jsapi_ticket,最后用jsapi_ticket和相关的参数按照ASCII码...
    • ABP开发指南系列教程(2) – 多层架构...
      为了减少复杂性和提高代码的可重用性,采用分层架构是一种被广泛接受的技术。为了实现分层的体系结构,ABP遵循DDD(领域驱动设计)的原则,将工程分为四个层: 展现层(...
    • ABP开发指南系列教程(1) – 入...
      ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。 ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点,它旨在成为一个通用的WE...
    • Windows下 JIRA + Agile + Mysql 破解...
      本文讲述了Windows下 JIRA + Agile + Mysql 破解安装示例教程
    • c#类的构造函数继承关系示例剖析
      本文通过示例代码讲解了c#子类的构造函数与父类的关系,子类怎样集成父类的构造函数的。