Chuyển tới nội dung chính

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}");
}
mẹo

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>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à foreach
  • yield return — compiler tạo iterator state machine
  • LINQ operator — Where, Select, OrderBy là 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 foreach là đủ
  • Chỉ có một cách duyệt — IEnumerable tí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>foreach
  • yield return là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>aggregate — có GetEnumerator() trả về IEnumerator<T>. IEnumerator<T>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>eager — tất cả phần tử trong bộ nhớ. IEnumerable<T> với yield returnlazy — 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()).

  • Visitor — thêm thao tác trong khi iterate
  • Composite — iterator thường duyệt cây composite
  • LINQ — iterator pipeline tích hợp của C#