關於 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
- 提供一種在不暴露集合底層形式(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 轉為程式碼
包含迭代器的集合的介面
/// <summary>
/// 包含迭代器的集合的介面
/// </summary>
public interface IAggregate
{
IIterator CreateIterator();
}
包含迭代器的集合實作
/// <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);
}
}
迭代器介面
/// <summary>
/// 迭代器介面
/// </summary>
public interface IIterator
{
object First();
object Next();
bool IsDone();
object CurrentItem();
}
迭代器實作
/// <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;
}
}
- 建立集合
aggregate
- 呼叫
aggregate.CreateIterator()
建立迭代器 - 使用迭代器遍歷集合
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();
}
執行結果
遍歷 ConcreteAggregate:
Item A
Item B
Item C
Item D
4. 情境
我們接到了一個取得員工資料的需求
- 員工資料中包含榮譽員工鎮店貓貓、鎮店狗狗
- 取得員工資料時,只需取得一般員工資料
※ 在高階程式語言中有更簡單的作法(e.g. foreach + where),此情境範例僅是為了更加深 Iterator 使用方式的印象
員工資料類與員工類型
/// <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; }
}
包含迭代器的員工的介面
/// <summary>
/// 包含迭代器的員工的介面
/// </summary>
public interface IEmployee
{
IIterator CreateIterator();
}
包含迭代器的員工實作
/// <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);
}
}
迭代器介面
/// <summary>
/// 迭代器介面
/// </summary>
public interface IIterator
{
Employee First();
Employee Next();
bool IsDone();
Employee CurrentItem();
}
迭代器實作,取得資料時排除榮譽員工
/// <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;
}
}
- 建立員工集合
- 呼叫
employees.CreateIterator()
建立迭代器 - 使用迭代器遍歷員工集合
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();
}
執行結果
Iterating over collection:
Id: 1, Name: Wayne
Id: 3, Name: Andy
完整程式碼
GitHub:Behavioral_04_Iterator