解讀設計模式----迭代器模式(Iterator Pattern),誰纔是迭代高手
當你在使用JavaScript開發客戶端應用的時候使用過for...in嗎?
2var obj;
3useForIn = function ()
4{
5 obj = (0,1,2,3,4,5,6,7,8,9);
6 for(var o in obj)
7 {
8 document.write(o);
9 }
10}
11</script>
當你在.NET Frameworks上使用C#,VB.NET,J#等語言開發應用的時候使用過foreach....in嗎?
2{
3 static void Main(string[] args)
4 {
5 List<int> list = new List<int>();
6 //--------初始化集合-----------
7 for (int i = 0; i < 10; i++)
8 {
9 list.Add(i);
10 }
11 //-------遍歷集合--------------
12 foreach (int i in list)
13 {
14 Console.WriteLine(i);
15 }
16 }
17}
如果你是一位Java發燒者,你在遍歷集合的時候是使用什麼方式呢?是Iterator?還是for?
2public class UseForIn {
3 public static void main(String[] args) {
4 List<String> list = new ArrayList<String>();
5 for (int i = 0; i < 10; i++) {
6 list.add("String:" + i);
7 }
8 //--------------使用Iterator遍歷集合---------------
9 Iterator it = list.iterator();
10 while(it.hasNext()){
11 System.out.println(it.next());
12 }
13 //------使用for.in(JDK 1.5以更高版本才能支持)-----
14
15 for (String s : list ){
16 System.out.println(s);
17 }
18 }
19}
二、解說迭代模式
我們先來看看迭代器模式的UML圖(下圖來至http://www.dofactory.com/)
可以說,.NET Frameworks中的每一個集合對象,都應用了Iterator模式。一個聚集對象,而且不管這些對象是什麼時候都需要遍歷的時候,我們都應該使用迭代器模式。另外在我們需要爲集合對象提供多種遍歷方式的時候也可以考慮用迭代器模式。
本來這個模式還是很有意思的,不過現今來看迭代器模式實用價格遠不如學習價值大了。因爲現在的高級編程語言如C#,Java等本身已經把這個模式做在語言中了。回到本文開始,我想你就能夠明白我爲什麼在文章的開始部分就發起提問?就拿C#的foreach....in語言來說吧,他就是一個可以遍歷所有的集合對象的工具,而且非常好用。
另外還有像IEnumerable接口也是爲迭代器模式而準備的,不管如何,學習一下GOF的迭代器模式的基本結構,還是很有學習價值的。研究歷史是爲了更好地迎接未來。
三、悟透foreach....in語句
爲了使用戶更方便的遍歷集合對象的所有元素,C#提供了foreach...in語句,該語句的實現正是通過IEnumerable的MoveNext()來完成遍歷的。
爲了驗證foreach....in語句與迭代器的關係,我們來定義一個實現逆序遍歷集合的類ReverseList類,定義很簡單,只需要繼承ArrayList類,並重寫GetEnumerator方法既可。
2{
3 public class ReverseList:ArrayList
4 {
5 public override IEnumerator GetEnumerator()
6 {
7 return new ReverseListEnumerator(this);
8 }
9 }
10}
其中,類ReverseListEnumerator實現了IEnumerator,它提供了逆序遍歷的迭代器。定義如下:
2using System.Collections.Generic;
3using System.Text;
4using System.Collections;
5
6namespace DesignPattern.Iterator
7{
8 public class ReverseListEnumerator:IEnumerator
9 {
10 public ReverseListEnumerator(ArrayList list)
11 {
12 this.list = list;
13 this.index = list.Count;
14 this.CurrentElement = list;
15 }
16
17 private object CurrentElement;
18 private int index;
19 private ArrayList list;
20
21 public object Current
22 {
23 get
24 {
25 object obj = this.CurrentElement;
26 if (obj != this.list)
27 {
28 return obj;
29 }
30 if (this.index == -1)
31 {
32 throw new Exception("索引超出下標範圍!");
33 }
34 }
35 }
36
37 public bool MoveNext()
38 {
39 if (this.index > 0)
40 {
41 this.index--;
42 this.CurrentElement = this.list[this.index];
43 return true;
44 }
45 this.CurrentElement = this.list;
46 this.index = 0;
47 return false;
48 }
49
50 public void Reset()
51 {
52 this.CurrentElement = this.list;
53 this.index = this.list.Count;
54 }
55 }
56}
57
我們來比較下使用ArrayList和自定義的ReverseList類通過foreach....in遍歷後的結果:
2using System.Collections.Generic;
3using System.Text;
4
5namespace DesignPattern.Iterator
6{
7 class Program
8 {
9 static void Main(string[] args)
10 {
11 List<int> list = new List<int>();
12 ReverseList rlist = new ReverseList();
13 //--------初始化數據-----------------------
14 for (int i = 0; i < 10; i++)
15 {
16 list.Add(i);
17 rlist.Add(i);
18 }
19 //-----使用C#的foreach.in語句(順序遍歷類ArrayList)-------
20 foreach (int i in list)
21 {
22 Console.Write(i + " ");
23 }
24 Console.WriteLine(); //起換行作用
25
26 //--------使用自定義的逆序遍歷類(ReverseList)--------------
27 foreach (int i in rlist)
28 {
29 Console.Write(i + " ");
30 }
31 Console.WriteLine(); //起換行作用
32 }
33 }
34}
運行結果如下:
四、現實生活中的迭代高手
我想對於大多數(有一部分人自己有車,有部分人騎自行車或摩托車,有部分人步行)的人來說,每天都有這樣的經歷,早晨起牀後一陣忙碌,忙完了就是準備上班了。來到了公交車站,XX分鐘過去後,到上班點的公交來了,這時該做什麼?上貝,不上你就等着上班遲到吧,哈哈。
仔細觀察售票員就會發現,每到一個站點,公交車都會停下上客或是下客,人多混雜(有的是買過票的,有的還沒買過),我真佩服售票員的記憶,乘客隨時都在上下,他總是能記住上了那些人,其中那些又是沒有買票的。他可以從車頭賣票到車尾,也可以從車尾到車頭,也可以就在車門那站着售票,呵呵,這好象也迭代器有很大的聯繫。看看下面代碼:
2{
3
4}
直接繼承ArrayList,什麼也不做,這時[公交車]也就擁有了ArrayList的公開屬性和方法。下面是[乘客類];
2using System.Collections.Generic;
3using System.Text;
4
5namespace DesignPattern.Iterator
6{
7 public class 乘客
8 {
9 public 乘客() { }
10 public 乘客(string name, bool flag)
11 {
12 this.name = name;
13 this.flag = flag;
14 }
15
16 private string name;
17 public string Name
18 {
19 get { return name; }
20 set { name = value; }
21 }
22
23 private bool flag;
24 public bool Flag
25 {
26 get { return flag; }
27 set { flag = value; }
28 }
29 }
30}
乘客名字和標識(是否買過票),看看下面的示例;
2{
3 class Program
4 {
5 static void Main(string[] args)
6 {
7 公交車 gjc = new 公交車();
8 乘客 ck = null;
9 //乘客上車
10 for (int i = 0; i < 5; i++)
11 {
12 ck = new 乘客(i + 1 + "號乘客", false);
13 gjc.Add(ck);
14 }
15 //迭代高手出場--收車費--全部收完
16 for (int i = 0; i < gjc.Count; i++)
17 {
18 乘客 c = (乘客)gjc[i];
19 c.Flag = true;
20 }
21 //到了一站--張三和李四上了車
22 //售票員還沒來收張三的錢呢,不會是在考驗張三是不是自覺掏錢買票吧
23 gjc.Add(new 乘客("張三", false));
24 gjc.Add(new 乘客("李四", true)); //這人老實,一上車就自動購票
25
26 foreach (乘客 k in gjc)
27 {
28 if (k.Flag)
29 {
30 Console.WriteLine("到終點站了,{0}請下車!", k.Name);
31 }
32 if (!k.Flag)
33 {
34 Console.WriteLine("你好{0}同志,你還沒有買票,要走可以,請先買票!", k.Name);
35 }
36 }
37 }
38 }
39}
上面的代碼很簡單,我就不做詳細解說,運行結果如下圖:
五、總結迭代高手
從上面的幾個示例中就可以看出,儘管我們沒有顯示的引用迭代器,但實質還是通過迭代器來遍歷的。總地來說,迭代器模式就是分離了集合對象的迭代行爲,抽象出一個迭代器類來負責,這樣既可做到不暴露集合的內部結構,又可以讓外部代碼可以透明的訪問集合內部的元素。
迭代器模式在訪問數組、集合、列表等數據時,尤其是數據庫數據操作時,是非常普遍的應用,但由於它太普遍了,所以各種高級語言都對他進行了封裝,所以反而給人感覺此模式本身不太常用了。
看來現實生活中的售票員就是一位了不起的迭代高手,每次乘客上下車他都會數數進行統計,然後根據他自己的迭代方式去遍歷車內的乘客進行售票,不會放過任何逃票之客。
任何行業都有技巧和經驗,需要多思考、多琢磨,才能做到最好的。
編程又何嘗不是這樣,沒有最好,只有更好,我們都需要努力。
注:上面總結源於《大話設計模式》
轉載請註明出處:http://beniao.cnblogs.com 或 http://www.cnblogs.com