Что такое паттерн проектирования «Итератор» (Iterator)?

«Что такое паттерн проектирования «Итератор» (Iterator)?» — вопрос из категории Паттерны, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Паттерн «Итератор» — это поведенческий паттерн, который предоставляет способ последовательного доступа к элементам составного объекта (коллекции, дерева, графа), не раскрывая его внутреннего представления.

Основная цель: Отделить алгоритм обхода коллекции от самой коллекции, что позволяет иметь разные алгоритмы обхода и делает клиентский код независимым от структуры данных.

Ключевые компоненты:

  1. IIterator<T> — интерфейс итератора с методами MoveNext(), Current и, возможно, Reset().
  2. IAggregate<T> / IEnumerable<T> — интерфейс агрегата (коллекции), который может создавать итератор (метод GetEnumerator()).
  3. Конкретный итератор (Concrete Iterator) — реализует логику обхода конкретной коллекции.
  4. Конкретная коллекция (Concrete Aggregate) — возвращает экземпляр конкретного итератора.

Пример реализации на C#:

// Пользовательский итератор для обхода коллекции в обратном порядке
public class ReverseIterator<T> : IIterator<T>
{
    private readonly List<T> _collection;
    private int _currentPosition;
    public ReverseIterator(List<T> collection)
    {
        _collection = collection;
        _currentPosition = collection.Count - 1;
    }
    public bool MoveNext() => _currentPosition >= 0;
    public T Current => _collection[_currentPosition--];
}

// Пользовательская коллекция
public class MyCollection<T> : IAggregate<T>
{
    private List<T> _items = new List<T>();
    public void Add(T item) => _items.Add(item);
    // Стандартный итератор (foreach)
    public IEnumerator<T> GetEnumerator() => _items.GetEnumerator();
    // Специальный итератор (обратный порядок)
    public IIterator<T> GetReverseIterator() => new ReverseIterator<T>(_items);
}

// Использование
var collection = new MyCollection<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("Standard iteration:");
foreach (var item in collection) Console.Write(item + " "); // 1 2 3 4 5
Console.WriteLine("nReverse iteration:");
var reverseIterator = collection.GetReverseIterator();
while (reverseIterator.MoveNext())
{
    Console.Write(reverseIterator.Current + " "); // 5 4 3 2 1
}

Встроенная поддержка в .NET: Паттерн глубоко интегрирован в язык C# через интерфейсы IEnumerable<T> и IEnumerator<T>. Ключевое слово foreach — это синтаксический сахар для работы с итератором.

// Внутренняя работа foreach
IEnumerator<int> enumerator = collection.GetEnumerator();
try
{
    while (enumerator.MoveNext())
    {
        int item = enumerator.Current;
        // ... работа с item
    }
}
finally
{
    enumerator.Dispose();
}

Преимущества:

  • Единый интерфейс обхода: Клиентский код работает с любыми коллекциями через единый интерфейс итератора.
  • Принцип единой ответственности: Коллекция отвечает за хранение данных, итератор — за логику обхода.
  • Параллельный обход: Можно создать несколько независимых итераторов для одной коллекции.
  • Отложенное выполнение (yield return): В C# с помощью yield return можно создавать итераторы с ленивым выполнением, что эффективно для работы с большими или генерируемыми на лету данными.