Iterator Pattern (Mẫu Iterator)
Định nghĩa (Definition)
Iterator cung cấp cách truy cập các phần tử của aggregate object tuần tự mà không phơi bày biểu diễn bên trong (Underlying Representation).
Ví dụ Coffee Shop
Quán cà phê lưu menu dưới dạng Dictionary (để tìm nhanh theo tên). Khách hàng muốn duyệt menu theo nhiều thứ tự — theo danh mục, theo giá (rẻ nhất trước), hoặc theo độ phổ biến. Ta tạo iterator riêng cho mỗi chiến lược duyệt.
Cấu trúc (Structure)
Iterator Interface
public interface IIterator<T>
{
bool HasNext();
T Next();
void Reset();
}
public enum Category { Coffee, Pastry, Beverage }
public record MenuItem(string Name, Category Category, decimal Price, int Popularity);
Concrete Iterator
public class MenuByCategoryIterator : IIterator<MenuItem>
{
private readonly List<MenuItem> _items;
private int _index;
public MenuByCategoryIterator(Dictionary<string, MenuItem> source)
{
_items = source.Values
.OrderBy(i => i.Category)
.ThenBy(i => i.Name)
.ToList();
}
public bool HasNext() => _index < _items.Count;
public MenuItem Next() => _items[_index++];
public void Reset() => _index = 0;
}
public class MenuByPriceIterator : IIterator<MenuItem>
{
private readonly List<MenuItem> _items;
private int _index;
public MenuByPriceIterator(Dictionary<string, MenuItem> source)
{
_items = source.Values.OrderBy(i => i.Price).ToList();
}
public bool HasNext() => _index < _items.Count;
public MenuItem Next() => _items[_index++];
public void Reset() => _index = 0;
}
Client
var menu = new CoffeeMenu();
menu.Add(new("Latte", Category.Coffee, 4.00m, 95));
menu.Add(new("Espresso", Category.Coffee, 2.50m, 80));
menu.Add(new("Croissant", Category.Pastry, 3.00m, 60));
// Duyệt theo giá
var priceIter = menu.CreatePriceIterator();
while (priceIter.HasNext())
{
var item = priceIter.Next();
Console.WriteLine($" ${item.Price:F2} {item.Name}");
}
Cấu trúc lưu trữ bên trong (Dictionary<string, MenuItem>) bị ẩn. Client duyệt menu qua iterator mà không biết item được lưu như thế nào.
C# tích hợp sẵn: IEnumerable và foreach
C# có Iterator tích hợp trong ngôn ngữ qua IEnumerable<T> và yield return:
public class CoffeeMenu : IEnumerable<MenuItem>
{
private readonly List<MenuItem> _items = new();
public void Add(MenuItem item) => _items.Add(item);
public IEnumerable<MenuItem> ByPrice() =>
_items.OrderBy(i => i.Price);
public IEnumerator<MenuItem> GetEnumerator() => _items.GetEnumerator();
}
// Dùng với foreach
foreach (var item in menu.ByPrice())
Console.WriteLine($"{item.Name}: ${item.Price:F2}");
Sử dụng thực tế trong .NET (.NET Real-World Usage)
IEnumerable<T>/IEnumerator<T>— nền tảng của LINQ vàforeachyield return— compiler tạo iterator state machine- LINQ operator —
Where,Select,OrderBylà iterator combinator IAsyncEnumerable<T>— iteration bất đồng bộ (await foreach)
Khi nào sử dụng (When to Use)
- Cần duyệt collection mà không phơi bày cấu trúc bên trong
- Cần nhiều chiến lược duyệt cho cùng một collection
- Muốn interface duyệt thống nhất cho nhiều loại collection
Khi nào KHÔNG sử dụng (When NOT to Use)
- Collection đơn giản và vòng
foreachlà đủ - Chỉ có một cách duyệt —
IEnumerabletích hợp là đủ - Cần random access — dùng index
Điểm chính (Key Takeaways)
- Iterator cung cấp truy cập tuần tự không phơi bày cấu trúc bên trong
- Nhiều iterator có thể cung cấp thứ tự duyệt khác nhau cho cùng data
- C# tích hợp sâu qua
IEnumerable<T>vàforeach yield returnlàm iterator tùy chỉnh trở nên dễ dàng
Câu hỏi phỏng vấn (Interview Questions)
Q: IEnumerable<T> khác IEnumerator<T> như thế nào?
IEnumerable<T> là aggregate — có GetEnumerator() trả về IEnumerator<T>. IEnumerator<T> là iterator — có MoveNext(), Current, và Reset(). foreach gọi GetEnumerator() bên trong.
Q: yield return hoạt động như thế nào?
Compiler biến method yield return thành state machine class implement IEnumerator<T>. Mỗi yield return là một điểm tạm dừng. Class được tạo theo dõi state giữa các lần gọi MoveNext().
Q: Eager iteration khác lazy iteration như thế nào?
List<T> là eager — tất cả phần tử trong bộ nhớ. IEnumerable<T> với yield return là lazy — phần tử được tạo khi cần. LINQ mặc định lazy — không execute cho đến khi enumerate (vd: foreach hoặc .ToList()).