# 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

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/)
---
## 新手上路,若有錯誤還請告知,謝謝