# 17. Iterator ###### tags: `DesignPatterns` ## 關於 Iterator 本篇將討論以下幾個問題 > ### 1. 關於 Iterator > ### 2. UML > ### 3. 將 UML 轉為程式碼 > ### 4. 情境 --- ## 測試環境: >OS:Windows 10 >IDE:Visual Studio 2019 --- ## 1. 關於 Iterator > Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. > > by [Gang of Four](https://en.wikipedia.org/wiki/Design_Patterns) - 提供一種在不暴露集合底層形式(Array、Queue、Stack等)的情況下遍歷集合中元素的方法 Iterator(迭代器)屬於行為型(Behavioral Patterns),當遇到**對外部隱藏複雜結構集合的實作**或**達到特定目的**,並提供遍歷接口時,可以使用 Iterator 來做為外部取用集合的接口,以提高安全和便利性。 優點: - 符合 單一職責原則(Single Responsibility Principle) - 符合 開閉原則(Open Closed Principle) 缺點: - 在特定集合類型使用迭代器可能會有效能問題 - 一般不會在迭代器中處理 新增/刪除,因維護上較麻煩 --- ## 2. UML ![](https://i.imgur.com/lZk5o7J.jpg) Class 間關聯: - Client 關聯 Iterator & Aggregate - ConcreteAggregate 繼承 Aggregate - ConcreteIterator 繼承 Iterator - ConcreteAggregate 可包含 ConcreteIterator - ConcreteIterator 關聯 ConcreteAggregate Class: - Client:呼叫端 - Iterator:迭代器抽象類別或介面,定義訪問或遍歷元素的接口 - ConcreteIterator:迭代器實作 - Aggregate:包含迭代器的集合的抽象類別或介面 - ConcreteAggregate:包含迭代器的集合實作 --- ## 3. 將 UML 轉為程式碼 包含迭代器的集合的介面 ```C# /// <summary> /// 包含迭代器的集合的介面 /// </summary> public interface IAggregate { IIterator CreateIterator(); } ``` 包含迭代器的集合實作 ```C# /// <summary> /// 包含迭代器的集合實作 /// </summary> public class ConcreteAggregate : IAggregate { private ArrayList _items = new ArrayList(); public IIterator CreateIterator() { return new ConcreteIterator(this); } public int ElementCount => _items.Count; // Indexer public object this[int index] { get => _items[index]; set => _items.Insert(index, value); } } ``` 迭代器介面 ```C# /// <summary> /// 迭代器介面 /// </summary> public interface IIterator { object First(); object Next(); bool IsDone(); object CurrentItem(); } ``` 迭代器實作 ```C# /// <summary> /// 迭代器實作 /// </summary> public class ConcreteIterator : IIterator { private ConcreteAggregate _aggregate { get; } private int _current = 0; public ConcreteIterator(ConcreteAggregate aggregate) { _aggregate = aggregate; } // 取得第一個元素 public object First() { return _aggregate[0]; } // 取得接續元素 public object Next() { object ret = null; if (_current < _aggregate.ElementCount - 1) { ret = _aggregate[++_current]; } return ret; } // 取得當前元素 public object CurrentItem() { return _aggregate[_current]; } // 是否為最後一個元素 public bool IsDone() { return _current >= _aggregate.ElementCount; } } ``` 1. 建立集合 `aggregate` 2. 呼叫`aggregate.CreateIterator()`建立迭代器 3. 使用迭代器遍歷集合 ```C# static void Main(string[] args) { Default.ConcreteAggregate aggregate = new Default.ConcreteAggregate { [0] = "Item A", [1] = "Item B", [2] = "Item C", [3] = "Item D" }; Default.IIterator iterator = aggregate.CreateIterator(); Console.WriteLine("遍歷 ConcreteAggregate:"); var item = iterator.First(); while (item != null) { Console.WriteLine(item); item = iterator.Next(); } Console.ReadLine(); } ``` 執行結果 ```Console 遍歷 ConcreteAggregate: Item A Item B Item C Item D ``` --- ## 4. 情境 我們接到了一個取得員工資料的需求 - 員工資料中包含榮譽員工鎮店貓貓、鎮店狗狗 - 取得員工資料時,只需取得一般員工資料 #### ※ 在高階程式語言中有更簡單的作法(e.g. foreach + where),此情境範例僅是為了更加深 Iterator 使用方式的印象 員工資料類與員工類型 ```C# /// <summary> /// 員工類型分為 一般員工 & 榮譽員工 /// </summary> public enum EmployeeType { General, Honours } /// <summary> /// 員工資料 /// </summary> public class Employee { public int Id { get; set; } public string Name { get; set; } public EmployeeType EmployeeType { get; set; } } ``` 包含迭代器的員工的介面 ```C# /// <summary> /// 包含迭代器的員工的介面 /// </summary> public interface IEmployee { IIterator CreateIterator(); } ``` 包含迭代器的員工實作 ```C# /// <summary> /// 包含迭代器的員工實作 /// </summary> public class Employees : IEmployee { // 此處並非範例重點,故直接指定容量為 10 private Employee[] _items = new Employee[10]; public IIterator CreateIterator() { return new ConcreteIterator(this); } public int ElementCount => _items.Length; // Indexer public Employee this[int index] { get => _items[index]; set =>_items.SetValue(value, index); } } ``` 迭代器介面 ```C# /// <summary> /// 迭代器介面 /// </summary> public interface IIterator { Employee First(); Employee Next(); bool IsDone(); Employee CurrentItem(); } ``` 迭代器實作,取得資料時排除榮譽員工 ```C# /// <summary> /// 迭代器實作 /// </summary> public class ConcreteIterator : IIterator { private Employees _aggregate { get; } private int _current = 0; public ConcreteIterator(Employees aggregate) { _aggregate = aggregate; } // 取得第一個元素 public Employee First() { var employee = _aggregate[0]; if (employee != null && employee.EmployeeType == EmployeeType.Honours) { Next(); } return employee; } // 取得接續元素 public Employee Next() { Employee employee = null; if (_current < _aggregate.ElementCount - 1) { employee = _aggregate[++_current]; } if (employee != null && employee.EmployeeType == EmployeeType.Honours) { return Next(); } return employee; } // 取得當前元素 public Employee CurrentItem() { return _aggregate[_current]; } // 是否為最後一個元素 public bool IsDone() { return _current >= _aggregate.ElementCount; } } ``` 1. 建立員工集合 2. 呼叫`employees.CreateIterator()`建立迭代器 3. 使用迭代器遍歷員工集合 ```C# static void Main(string[] args) { Situation.Employees employees = new Situation.Employees { [0] = new Situation.Employee { Id = 1, Name = "Wayne", EmployeeType = Situation.EmployeeType.General}, [1] = new Situation.Employee { Id = 2, Name = "Dog", EmployeeType = Situation.EmployeeType.Honours }, [2] = new Situation.Employee { Id = 3, Name = "Andy", EmployeeType = Situation.EmployeeType.General }, [3] = new Situation.Employee { Id = 4, Name = "Cat", EmployeeType = Situation.EmployeeType.Honours }, }; Situation.IIterator iterator = employees.CreateIterator(); Console.WriteLine("Iterating over collection:"); var item = iterator.First(); while (item != null) { Console.WriteLine($"Id: {item.Id}, Name: {item.Name}"); item = iterator.Next(); } Console.ReadLine(); } ``` 執行結果 ```Console Iterating over collection: Id: 1, Name: Wayne Id: 3, Name: Andy ``` --- ## 完整程式碼 GitHub:[Behavioral_04_Iterator](https://github.com/darionnnnnn/blog/tree/master/Blog/Behavioral_04_Iterator) --- ## 總結 ### 其實在高階程式語言中的集合都已經在底層實作 Iterator 了,所以我們才能夠直接對集合進行 foreach 操作,除了特定目的(e.g. 情境範例中的排除特定目標)之外,基本上也很少有機會需要自己實作 Iterator 了。 --- ## 參考資料 1. [Design Patterns](https://en.wikipedia.org/wiki/Design_Patterns) 2. [大話設計模式](https://www.tenlong.com.tw/products/9789866761799) 2. [dofactory](https://www.dofactory.com/) 3. [Refactoring.Guru](https://refactoring.guru/) --- ## 新手上路,若有錯誤還請告知,謝謝