1. 程式人生 > >設計模式(16)——迭代器模式(Iterator Pattern)

設計模式(16)——迭代器模式(Iterator Pattern)

一、引言

在上篇博文中分享了我對命令模式的理解,命令模式主要是把行為進行抽象成命令,使得請求者的行為和接受者的行為形成低耦合。在一章中,將介紹一下迭代器模式。下面廢話不多說了,直接進入本博文的主題。

二、迭代器模式的介紹

迭代器是針對集合物件而生的,對於集合物件而言,必然涉及到集合元素的新增刪除操作,同時也肯定支援遍歷集合元素的操作,我們此時可以把遍歷操作也放在集合物件中,但這樣的話,集合物件就承擔太多的責任了,面向物件設計原則中有一條是單一職責原則,所以我們要儘可能地分離這些職責,用不同的類去承擔不同的職責。迭代器模式就是用迭代器類來承擔遍歷集合元素的職責。

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 // 抽象聚合類 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 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語句只能在對集合進行遍歷,不能在遍歷的同時更改集合中的元素。

六、總結

到這裡,本博文的內容就介紹結束了,迭代器模式就是抽象一個迭代器類來分離了集合物件的遍歷行為,這樣既可以做到不暴露集合的內部結構,又可讓外部程式碼透明地訪問集合內部的資料。在一篇博文中將為大家介紹觀察者模式。