一、引言
今天我们开始讲“行为型”设计模式的第三个模式,该模式是【迭代器模式】,英文名称是: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、迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。
3.1】、迭代器模式的优点:
(1)、迭代器模式使得访问一个聚合对象的内容而无需暴露它的内部表示,即迭代抽象。
(2)、迭代器模式为遍历不同的集合结构提供了一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作
3.2】、迭代器模式的缺点:
(1)、迭代器模式在遍历的同时更改迭代器所在的集合结构会导致出现异常。所以使用foreach语句只能在对集合进行遍历,不能在遍历的同时更改集合中的元素。
3.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 Length 102 { 103 [__DynamicallyInvokable] 104 get 105 { 106 return this.m_length; 107 } 108 [__DynamicallyInvokable] 109 set 110 { 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 Count 138 { 139 get 140 { 141 return this.m_length; 142 } 143 } 144 145 public object SyncRoot 146 { 147 get 148 { 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 IsReadOnly 158 { 159 get 160 { 161 return false; 162 } 163 } 164 165 public bool IsSynchronized 166 { 167 get 168 { 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 8 211 }), "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 32 272 }), "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 else 316 { 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 else 440 { 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_length 464 }; 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 框架中的实现,受益匪浅。