[聚合文章] C#设计模式之十六迭代器模式(Iterator Pattern)【行为型】

.Net 2017-11-27 15 阅读

一、引言

   今天我们开始讲“行为型”设计模式的第三个模式,该模式是【迭代器模式】,英文名称是:Iterator Pattern。还是老套路,先从名字上来看看。“迭代器模式”我第一次看到这个名称,我的理解是,迭代是遍历的意思,迭代器可以理解为是遍历某某的工具,遍历什么呢?在软件设计中,当然遍历的是集合对象,所以说迭代器模式是遍历集合的一种通用的算法。如果集合只有一种类型,那这个模式就没用了,就是因为集合对象包含数组、列表,字典和哈希表等各种对象,如果为每一种集合对象都实现一套遍历算法,也不太现实,因此为了解决遍历集合有一个统一的接口这个事情,所以就提出了“迭代器”这个模式。

二、迭代器模式的详细介绍

2.1、动机(Motivate)

   在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能。

 使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方式。

2.2、意图(Intent)

   提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。                                      ——《设计模式》GoF

2.3、结构图

      

2.4、模式的组成
    
    从迭代器模式的结构图可以看出,它涉及到四个角色,它们分别是:

    (1)、抽象迭代器(Iterator):抽象迭代器定义了访问和遍历元素的接口,一般声明如下方法:用于获取第一个元素的first(),用于访问下一个元素的next(),用于判断是否还有下一个元素的hasNext(),用于获取当前元素的currentItem(),在其子类中将实现这些方法。

    (2)、具体迭代器(ConcreteIterator):具体迭代器实现了抽象迭代器接口,完成对集合对象的遍历,同时在对聚合进行遍历时跟踪其当前位置。

    (3)、抽象聚合类(Aggregate):抽象聚合类用于存储对象,并定义创建相应迭代器对象的接口,声明一个createIterator()方法用于创建一个迭代器对象。

    (4)、具体聚合类(ConcreteAggregate):具体聚合类实现了创建相应迭代器的接口,实现了在抽象聚合类中声明的createIterator()方法,并返回一个与该具体聚合相对应的具体迭代器ConcreteIterator实例。

2.5、迭代器模式的代码实现

    迭代器模式在现实生活中也有类似的例子,比如:在部队中,我们可以让某一队伍当中的某人出列,或者让队列里面的每个人依次报名,其实这个过程就是一个遍历的过程。没什么可说的,具体实现代码如下:

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

  
三、迭代器模式的实现要点:
    
     (1)、迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示。

     (2)、迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。

     (3)、迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。

     适用性

       (1)、访问一个聚合对象的内容而无需暴露它的内部表示。

       (2)、支持对聚合对象的多种遍历。

       (3)、为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。


四、.NET 中迭代器模式的实现

     在mscorlib程序集里有这样一个命名空间,该命名空间就是:System.Collections,在该命名空间里面早已有了迭代器模式的实现。对于聚集接口和迭代器接口已经存在了,其中IEnumerator扮演的就是迭代器的角色,它的实现如下:

public interface IEnumerator {      object Current      {           get;      }     bool MoveNext();     void Reset(); }


     属性Current返回当前集合中的元素,Reset()方法恢复初始化指向的位置,MoveNext()方法返回值true表示迭代器成功前进到集合中的下一个元素,返回值false表示已经位于集合的末尾。能够提供元素遍历的集合对象,在.Net中都实现了IEnumerator接口。

     IEnumerable则扮演的就是抽象聚集的角色,只有一个GetEnumerator()方法,如果集合对象需要具备跌代遍历的功能,就必须实现该接口。

public interface IEnumerable{    IEumerator GetEnumerator();}

    抽象聚合角色(Aggregate)和抽象迭代器角色(Iterator)分别是IEnumerable接口和IEnumerator接口,具体聚合角色(ConcreteAggregate)有Queue类型, BitArray等类型,代码如下:

  1     public sealed class BitArray : ICollection, IEnumerable, ICloneable  2     {  3         [Serializable]  4         private class BitArrayEnumeratorSimple : IEnumerator, ICloneable  5         {  6             private BitArray bitarray;  7   8             private int index;  9  10             private int version; 11  12             private bool currentElement; 13  14             public virtual object Current 15             { 16                 get 17                 { 18                     if (this.index == -1) 19                     { 20                         throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumNotStarted")); 21                     } 22                     if (this.index >= this.bitarray.Count) 23                     { 24                         throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumEnded")); 25                     } 26                     return this.currentElement; 27                 } 28             } 29  30             internal BitArrayEnumeratorSimple(BitArray bitarray) 31             { 32                 this.bitarray = bitarray; 33                 this.index = -1; 34                 this.version = bitarray._version; 35             } 36  37             public object Clone() 38             { 39                 return base.MemberwiseClone(); 40             } 41  42             public virtual bool MoveNext() 43             { 44                 if (this.version != this.bitarray._version) 45                 { 46                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion")); 47                 } 48                 if (this.index < this.bitarray.Count - 1) 49                 { 50                     this.index++; 51                     this.currentElement = this.bitarray.Get(this.index); 52                     return true; 53                 } 54                 this.index = this.bitarray.Count; 55                 return false; 56             } 57  58             public void Reset() 59             { 60                 if (this.version != this.bitarray._version) 61                 { 62                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion")); 63                 } 64                 this.index = -1; 65             } 66         } 67  68         private const int BitsPerInt32 = 32; 69  70         private const int BytesPerInt32 = 4; 71  72         private const int BitsPerByte = 8; 73  74         private int[] m_array; 75  76         private int m_length; 77  78         private int _version; 79  80         [NonSerialized] 81         private object _syncRoot; 82  83         private const int _ShrinkThreshold = 256; 84  85         [__DynamicallyInvokable] 86         public bool this[int index] 87         { 88             [__DynamicallyInvokable] 89             get 90             { 91                 return this.Get(index); 92             } 93             [__DynamicallyInvokable] 94             set 95             { 96                 this.Set(index, value); 97             } 98         } 99 100         [__DynamicallyInvokable]101         public int Length102         {103             [__DynamicallyInvokable]104             get105             {106                 return this.m_length;107             }108             [__DynamicallyInvokable]109             set110             {111                 if (value < 0)112                 {113                     throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));114                 }115                 int arrayLength = BitArray.GetArrayLength(value, 32);116                 if (arrayLength > this.m_array.Length || arrayLength + 256 < this.m_array.Length)117                 {118                     int[] array = new int[arrayLength];119                     Array.Copy(this.m_array, array, (arrayLength > this.m_array.Length) ? this.m_array.Length : arrayLength);120                     this.m_array = array;121                 }122                 if (value > this.m_length)123                 {124                     int num = BitArray.GetArrayLength(this.m_length, 32) - 1;125                     int num2 = this.m_length % 32;126                     if (num2 > 0)127                     {128                         this.m_array[num] &= (1 << num2) - 1;129                     }130                     Array.Clear(this.m_array, num + 1, arrayLength - num - 1);131                 }132                 this.m_length = value;133                 this._version++;134             }135         }136 137         public int Count138         {139             get140             {141                 return this.m_length;142             }143         }144 145         public object SyncRoot146         {147             get148             {149                 if (this._syncRoot == null)150                 {151                     Interlocked.CompareExchange<object>(ref this._syncRoot, new object(), null);152                 }153                 return this._syncRoot;154             }155         }156 157         public bool IsReadOnly158         {159             get160             {161                 return false;162             }163         }164 165         public bool IsSynchronized166         {167             get168             {169                 return false;170             }171         }172 173         private BitArray()174         {175         }176 177         [__DynamicallyInvokable]178         public BitArray(int length) : this(length, false)179         {180         }181 182         [__DynamicallyInvokable]183         public BitArray(int length, bool defaultValue)184         {185             if (length < 0)186             {187                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));188             }189             this.m_array = new int[BitArray.GetArrayLength(length, 32)];190             this.m_length = length;191             int num = defaultValue ? -1 : 0;192             for (int i = 0; i < this.m_array.Length; i++)193             {194                 this.m_array[i] = num;195             }196             this._version = 0;197         }198 199         [__DynamicallyInvokable]200         public BitArray(byte[] bytes)201         {202             if (bytes == null)203             {204                 throw new ArgumentNullException("bytes");205             }206             if (bytes.Length > 268435455)207             {208                 throw new ArgumentException(Environment.GetResourceString("Argument_ArrayTooLarge", new object[]209                 {210                     8211                 }), "bytes");212             }213             this.m_array = new int[BitArray.GetArrayLength(bytes.Length, 4)];214             this.m_length = bytes.Length * 8;215             int num = 0;216             int num2 = 0;217             while (bytes.Length - num2 >= 4)218             {219                 this.m_array[num++] = ((int)(bytes[num2] & 255) | (int)(bytes[num2 + 1] & 255) << 8 | (int)(bytes[num2 + 2] & 255) << 16 | (int)(bytes[num2 + 3] & 255) << 24);220                 num2 += 4;221             }222             switch (bytes.Length - num2)223             {224             case 1:225                 goto IL_103;226             case 2:227                 break;228             case 3:229                 this.m_array[num] = (int)(bytes[num2 + 2] & 255) << 16;230                 break;231             default:232                 goto IL_11C;233             }234             this.m_array[num] |= (int)(bytes[num2 + 1] & 255) << 8;235             IL_103:236             this.m_array[num] |= (int)(bytes[num2] & 255);237             IL_11C:238             this._version = 0;239         }240 241         [__DynamicallyInvokable]242         public BitArray(bool[] values)243         {244             if (values == null)245             {246                 throw new ArgumentNullException("values");247             }248             this.m_array = new int[BitArray.GetArrayLength(values.Length, 32)];249             this.m_length = values.Length;250             for (int i = 0; i < values.Length; i++)251             {252                 if (values[i])253                 {254                     this.m_array[i / 32] |= 1 << i % 32;255                 }256             }257             this._version = 0;258         }259 260         [__DynamicallyInvokable]261         public BitArray(int[] values)262         {263             if (values == null)264             {265                 throw new ArgumentNullException("values");266             }267             if (values.Length > 67108863)268             {269                 throw new ArgumentException(Environment.GetResourceString("Argument_ArrayTooLarge", new object[]270                 {271                     32272                 }), "values");273             }274             this.m_array = new int[values.Length];275             this.m_length = values.Length * 32;276             Array.Copy(values, this.m_array, values.Length);277             this._version = 0;278         }279 280         [__DynamicallyInvokable]281         public BitArray(BitArray bits)282         {283             if (bits == null)284             {285                 throw new ArgumentNullException("bits");286             }287             int arrayLength = BitArray.GetArrayLength(bits.m_length, 32);288             this.m_array = new int[arrayLength];289             this.m_length = bits.m_length;290             Array.Copy(bits.m_array, this.m_array, arrayLength);291             this._version = bits._version;292         }293 294         [__DynamicallyInvokable]295         public bool Get(int index)296         {297             if (index < 0 || index >= this.Length)298             {299                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));300             }301             return (this.m_array[index / 32] & 1 << index % 32) != 0;302         }303 304         [__DynamicallyInvokable]305         public void Set(int index, bool value)306         {307             if (index < 0 || index >= this.Length)308             {309                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));310             }311             if (value)312             {313                 this.m_array[index / 32] |= 1 << index % 32;314             }315             else316             {317                 this.m_array[index / 32] &= ~(1 << index % 32);318             }319             this._version++;320         }321 322         [__DynamicallyInvokable]323         public void SetAll(bool value)324         {325             int num = value ? -1 : 0;326             int arrayLength = BitArray.GetArrayLength(this.m_length, 32);327             for (int i = 0; i < arrayLength; i++)328             {329                 this.m_array[i] = num;330             }331             this._version++;332         }333 334         [__DynamicallyInvokable]335         public BitArray And(BitArray value)336         {337             if (value == null)338             {339                 throw new ArgumentNullException("value");340             }341             if (this.Length != value.Length)342             {343                 throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));344             }345             int arrayLength = BitArray.GetArrayLength(this.m_length, 32);346             for (int i = 0; i < arrayLength; i++)347             {348                 this.m_array[i] &= value.m_array[i];349             }350             this._version++;351             return this;352         }353 354         [__DynamicallyInvokable]355         public BitArray Or(BitArray value)356         {357             if (value == null)358             {359                 throw new ArgumentNullException("value");360             }361             if (this.Length != value.Length)362             {363                 throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));364             }365             int arrayLength = BitArray.GetArrayLength(this.m_length, 32);366             for (int i = 0; i < arrayLength; i++)367             {368                 this.m_array[i] |= value.m_array[i];369             }370             this._version++;371             return this;372         }373 374         [__DynamicallyInvokable]375         public BitArray Xor(BitArray value)376         {377             if (value == null)378             {379                 throw new ArgumentNullException("value");380             }381             if (this.Length != value.Length)382             {383                 throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));384             }385             int arrayLength = BitArray.GetArrayLength(this.m_length, 32);386             for (int i = 0; i < arrayLength; i++)387             {388                 this.m_array[i] ^= value.m_array[i];389             }390             this._version++;391             return this;392         }393 394         [__DynamicallyInvokable]395         public BitArray Not()396         {397             int arrayLength = BitArray.GetArrayLength(this.m_length, 32);398             for (int i = 0; i < arrayLength; i++)399             {400                 this.m_array[i] = ~this.m_array[i];401             }402             this._version++;403             return this;404         }405 406         public void CopyTo(Array array, int index)407         {408             if (array == null)409             {410                 throw new ArgumentNullException("array");411             }412             if (index < 0)413             {414                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));415             }416             if (array.Rank != 1)417             {418                 throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));419             }420             if (array is int[])421             {422                 Array.Copy(this.m_array, 0, array, index, BitArray.GetArrayLength(this.m_length, 32));423                 return;424             }425             if (array is byte[])426             {427                 int arrayLength = BitArray.GetArrayLength(this.m_length, 8);428                 if (array.Length - index < arrayLength)429                 {430                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));431                 }432                 byte[] array2 = (byte[])array;433                 for (int i = 0; i < arrayLength; i++)434                 {435                     array2[index + i] = (byte)(this.m_array[i / 4] >> i % 4 * 8 & 255);436                 }437                 return;438             }439             else440             {441                 if (!(array is bool[]))442                 {443                     throw new ArgumentException(Environment.GetResourceString("Arg_BitArrayTypeUnsupported"));444                 }445                 if (array.Length - index < this.m_length)446                 {447                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));448                 }449                 bool[] array3 = (bool[])array;450                 for (int j = 0; j < this.m_length; j++)451                 {452                     array3[index + j] = ((this.m_array[j / 32] >> j % 32 & 1) != 0);453                 }454                 return;455             }456         }457 458         public object Clone()459         {460             return new BitArray(this.m_array)461             {462                 _version = this._version,463                 m_length = this.m_length464             };465         }466 467         [__DynamicallyInvokable]468         public IEnumerator GetEnumerator()469         {470             return new BitArray.BitArrayEnumeratorSimple(this);471         }472 473         private static int GetArrayLength(int n, int div)474         {475             if (n <= 0)476             {477                 return 0;478             }479             return (n - 1) / div + 1;480         }481     }

   还有很多类型,就不一一列举了,大家可以查看源代码,每个元素都可以在迭代器模式的构造图上找到对应的元素。

   具体的类型和代码截图如下:

     
  
五、总结

    今天到此就把迭代器模式写完了,该模式不是很难,结构也不是很复杂,Net框架里面也有现成的实现。并且在 Net 2.0里面还有升级的实现,要想学习该模式,可以好好看看该模式在Net 框架中的实现,受益匪浅。

注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。