相關閱讀 |
>>> 技術話題—商業文明的嶄新時代 >>> | 簡體 傳統 |
2006.7.12 李建忠
集合內部結構與外部訪問
動機(Motivation)
在軟件構建過程中,集合對象內部結構常常變化各異。但對于這些集合對象,我們希望在不暴露其內部結構的同時,可以讓外部客戶代碼透明地訪問其中包含的元素;同時這種“透明遍歷”也為“同一種算法在多種集合對象上進行操作”提供了可能。
使用面向對象技術將這種遍歷機制抽象為“迭代器對象”為“應對變化中的集合對象”提供了一種優雅的方式。
意圖(Intent)
提供一種方法順序訪問一個聚合對象中的各個元素,而又不暴露該對象的內部表示。
——《設計模式》GoF
例說Iterator應用
下面是迭代器所擁有的最小集合,Current是屬性,只能Get不能Set。還有兩個方法:MoveNext是往下一個元素走,如果訪問到最后一個元素之后沒有元素了,就返回False;Reset是復位,回到初始位置。
如果有一個容器實現了IEnumerable接口,它就可以支持我們的迭代操作了。
這種設計模式已經內化為C#語言的一種元素了,就是Foreach關鍵字。
我們定義的容器首先要實現IEnumerable接口,實現的GetEnumerator方法要返回一個IEnumerator的類型的集合。
Foreach的工作機制
遍歷代碼中訪問的全部是接口,而不用關心集合的內部結構。上面的代碼等同于下面的Foreach。
MyEnumerator集合類可以寫成private私有的,外部不可以訪問,只暴露接口給外部訪問。
結構(Structure)
Aggregate表示集合結構接口,對應例子中的IEnumerable,Iterator表示迭代器接口,對應IEnumerator。First()方法對應Reset(),Next()和IsDone對應MoveNext(),CurrentItem()對應Current屬性。Aggregate和Iterator的具體實現都只依賴于接口,而不依賴于具體實現,這樣就把兩者之間的耦合降低到最小。
Iterator模式的幾個要點
迭代抽象:訪問一個聚合對象的內容而無需暴露它的內部表示。
迭代多態:為遍歷不同的集合結構提供一個統一的接口,從而支持同樣的算法在不同的集合結構上進行操作。
例如假設我們有一個求和算法
它可以操作在多個支持迭代器的集合上,如果把這個算法寫成ArrayList的話,就會非常受限制。同時我們更可以用C#的Foreach語句來寫。
我們的算法應該是獨立的,寫的時候應該盡量操作接口,這樣我們寫好一個算法,就能應對N種集合的變化,使得同樣的算法能在不同的集合上操作。
迭代器的健壯性考慮:遍歷的同時更改迭代器所在的集合結構,會導致問題。
也就是說,我們在迭代的時候,應該只是讀取操作,不能更改容器的接口,例如遍歷的時候刪除一個元素,這樣是不可以的。容器的結構師絕對不能碰的,一旦結構更改,遍歷就會出問題。
下面這種情況對i進行更改,是不會影響原來的數組內容的,因為i是int類型,它只是一份拷貝。
我們一定要給用戶提供盡量純只讀的迭代。保證每個元素被且只被遍歷一次。
.NET 2.0中的Iterator
yield return關鍵字能夠支持迭代的簡化,動態生成迭代的類型。如果反編譯的話,能看見.NET內部能夠幫助我們構造一個之前我們寫的類,實現相應方法。雖然實現層面有所不同,但還是遵循了整體的迭代器定義的接口。能夠支持迭代的。
目前我們討論的迭代器游標都是往前走的,但我們也可以擴充方法,讓游標可以往回走,也可以設定游標的步長等等。
2010.10.17
MSDN 網絡廣播 李建忠 2013-08-22 08:51:09
稱謂:
内容: