我們嘗試建立一個簡易版的鏈結串列。
設計一個列表類別
先建立一個節點類別:
1 2 3 4 5 6 7 8 9 10
| public class SimpleNode { public string NodeName { get; set; } public SimpleNode Next { get; set; } public SimpleNode(string name) { NodeName = name; } }
|
以及一個串列類別:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class SimpleList { public SimpleNode Root { get; set; } public SimpleNode Last { get; set; } public void AddNode(SimpleNode node) { if (Root == null) { Root = node; Last = Root; } else { Last.Next = node; Last = Last.Next; } } }
|
然後,我們就可以建立一個簡單的列表,並且印出所有節點內容:
1 2 3 4 5 6 7 8 9 10 11 12
| var list = new SimpleList(); list.AddNode(new SimpleNode("node1")); list.AddNode(new SimpleNode("node2")); list.AddNode(new SimpleNode("node3"));
var current = list.Root; while (current != null) { Console.Write(current.NodeName); current = current.Next; }
|
但是,這樣的操作行為會暴露出資料結構,如果內部換成陣列,所有的操作方法都必須改變,因此我們必須將目前的行為抽象化,這就是反覆器模式。
使用反覆器模式
先建立一個反覆器介面,加入拜訪節點的行為介面:
1 2 3 4 5
| public interface Iterator { bool HasNext(); SimpleNode Next(); }
|
再來我們建立第二代類別 SimpleList2,繼承 SimpleList,加入一些屬性,並且實作 Iterator 介面:
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
| public class SimpleList2 : SimpleList, Iterator { public SimpleNode Current { get; set; } public bool IsDoing { get; set; }
public SimpleList2() { IsDoing = false; }
public bool HasNext() { if (IsDoing) { Current = Current.Next; } else { Current = Root; IsDoing = true; }
IsDoing = Current != null; return IsDoing; }
public SimpleNode Next() { return Current; } }
|
然後,我們一樣建立一個列表,並且印出所有節點內容:
1 2 3 4 5 6 7 8 9 10 11 12
| var list = new SimpleList2(); list.AddNode(new SimpleNode("node1")); list.AddNode(new SimpleNode("node2")); list.AddNode(new SimpleNode("node3"));
Iterator iterator = list; while (iterator.HasNext()) { var node = iterator.Next(); Console.Write(node.NodeName); }
|
這樣一來,我們就可以使用抽象的行為來拜訪列表節點,客戶端程式不必考慮複雜的資料結構,只要知道行為就好了,這就是設計模式中的反覆器模式。現在的高階程式語言皆已實作在集合類別中,像是 C# 的集合物件都可以用 foreach 語法進行拜訪,這就是反覆器模式的威力。
不過,在上面的例子,我們設計的集合類別還無法使用 foreach 語法,是因為沒有實作 GetEnumerator 方法,所以我們再來做一些修改。
讓自己的列表適用 foreach 語法
我們先建立第三代類別 SimpleList3,一樣是繼承 SimpleList,然後和 SimpleList2 不同的是,這裡只寫一個方法 GetEnumerator,建立並且回傳一個 Enumerator 物件:
1 2 3 4 5 6 7 8
| public class SimpleList3 : SimpleList { public IEnumerator<SimpleNode> GetEnumerator() { IEnumerator<SimpleNode> enumerator = new SimpleEnumerator(Root); return enumerator; } }
|
然後,我們建立一個 SimpleEnumerator 類別,必須實作 IEnumerator 介面:
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
| public class SimpleEnumerator : IEnumerator<SimpleNode> { public SimpleNode Root { get; set; } public SimpleNode Current { get; set; } public bool IsDoing { get; set; } object IEnumerator.Current { get { return Current; } }
public SimpleEnumerator(SimpleNode root) { Root = root; IsDoing = false; }
public void Dispose() { Root = null; Current = null; }
public bool MoveNext() { if (IsDoing) { Current = Current.Next; } else { Current = Root; IsDoing = true; }
IsDoing = Current != null; return IsDoing; }
public void Reset() { IsDoing = false; } }
|
然後,我們再次建立列表物件,並使用 foreach 語法拜訪列表節點:
1 2 3 4 5 6 7 8 9 10
| var list = new SimpleList3(); list.AddNode(new SimpleNode("node1")); list.AddNode(new SimpleNode("node2")); list.AddNode(new SimpleNode("node3"));
foreach (var node in list) { Console.Write(node.NodeName); }
|
結論
雖然高階程式語言都已經實作反覆器模式,但是對於程序員來說,了解其中原理仍然是非常重要的。