Итератор (Iterator)

Последнее обновление: 31.10.2015

Паттерн Итератор (Iterator) предоставляет абстрактный интерфейс для последовательного доступа ко всем элементам составного объекта без раскрытия его внутренней структуры.

Наверное, всем программистам, работающим с языком C#, приходилось иметь дело с циклом foreach, который перебирает объекты в массиве или коллекции. При этом встроенных классов коллекций существует множество, и каждая из них отличается по своей структуре и поведению.

Ключевым моментом, который позволяет осуществить перебор коллекций с помощью foreach, является применения этими классами коллекций паттерна итератор, или проще говоря пары интерфейсов IEnumerable / IEnumerator. Интерфейс IEnumerator определяет функционал для перебора внутренних объектов в контейнере:

public interface IEnumerator
{
    bool MoveNext(); // перемещение на одну позицию вперед в контейнере элементов
    object Current {get;}  // текущий элемент в контейнере
    void Reset(); // перемещение в начало контейнера
}

А интерфейс IEnumerable использует IEnumerator для получения итератора для конкретного типа объекта:

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}

Используя данные интерфейсы, мы можем свести к одному шаблону - с помощью цикла foreach - любые составные объекты.

Когда использовать итераторы?

  • Когда необходимо осуществить обход объекта без раскрытия его внутренней структуры

  • Когда имеется набор составных объектов, и надо обеспечить единый интерфейс для их перебора

  • Когда необходимо предоставить несколько альтернативных вариантов перебора одного и того же объекта

С помощью схем UML итераторы можно описать так:

Итераторы в C# и .NET

Формальное определение паттерна на C# может выглядеть следующим образом:

class Client
{
    public void Main()
    {
        Aggregate a = new ConcreteAggregate();
            
        Iterator i = a.CreateIterator();

        object item = i.First();
        while (!i.IsDone())
        {
            item = i.Next();
        }
    }
}

abstract class Aggregate
{
    public abstract Iterator CreateIterator();
    public abstract int Count { get; protected set; }
    public abstract object this[int index] { get; set; }
}
 
class ConcreteAggregate : Aggregate
{
    private readonly ArrayList _items = new ArrayList();
 
    public override Iterator CreateIterator()
    {
        return new ConcreteIterator(this);
    }
 
    public override int Count
    {
        get { return _items.Count; }
        protected set { }
    }

    public override object this[int index]
    {
        get { return _items[index]; }
        set { _items.Insert(index, value); }
    }
}
abstract class Iterator
{
    public abstract object First();
    public abstract object Next();
    public abstract bool IsDone();
    public abstract object CurrentItem();
}
 
class ConcreteIterator : Iterator
{
    private readonly Aggregate _aggregate;
    private int _current;
 
    public ConcreteIterator(Aggregate aggregate)
    {
        this._aggregate = aggregate;
    }
 
    public override object First()
    {
        return _aggregate[0];
    }
 
    public override object Next()
    {
        object ret = null;
 
        _current++;
 
        if (_current < _aggregate.Count)
        {
            ret = _aggregate[_current];
        }
 
        return ret;
    }
 
    public override object CurrentItem()
    {
        return _aggregate[_current];
    }
 
    public override bool IsDone()
    {
        return _current >= _aggregate.Count;
    }
}

Участники

  • Iterator: определяет интерфейс для обхода составных объектов

  • Aggregate: определяет интерфейс для создания объекта-итератора

  • ConcreteIterator: конкретная реализация итератора для обхода объекта Aggregate. Для фиксации индекса текущего перебираемого элемента использует целочисленную переменную _current

  • ConcreteAggregate: конкретная реализация Aggregate. Хранит элементы, которые надо будет перебирать

  • Client: использует объект Aggregate и итератор для его обхода

Теперь рассмотрим конкретный пример. Допустим, у нас есть классы книги и библиотеки:

class Book
{
    public string Name { get; set; }
}
class Library
{
    private Book[] books;
}

И, допустим, у нас есть класс читателя, который хочет получить информацию о книгах, которые находятся в библиотеке. И для этого надо осуществить перебор объектов с помощью итератора:

class Program
{
    static void Main(string[] args)
    {
        Library library = new Library();
        Reader reader = new Reader();
        reader.SeeBooks(library);

        Console.Read();
    }
}

class Reader
{
    public void SeeBooks(Library library)
    {
        IBookIterator iterator = library.CreateNumerator();
        while(iterator.HasNext())
        {
            Book book = iterator.Next();
            Console.WriteLine(book.Name);
        }
    }
}

interface IBookIterator
{
    bool HasNext();
    Book Next();
}
interface IBookNumerable
{
    IBookIterator CreateNumerator();
    int Count { get; }
    Book this[int index] { get;}
}
class Book
{
    public string Name { get; set; }
}

class Library : IBookNumerable
{
    private Book[] books;
    public Library()
    {
        books = new Book[]
        {
            new Book{Name="Война и мир"},
            new Book {Name="Отцы и дети"},
            new Book {Name="Вишневый сад"}
        };
    }
    public int Count
    {
        get { return books.Length; }
    }

    public Book this[int index]
    {
        get { return books[index]; }
    }
    public IBookIterator CreateNumerator()
    {
        return new LibraryNumerator(this);
    }
}
class LibraryNumerator : IBookIterator
{
    IBookNumerable aggregate;
    int index=0;
    public LibraryNumerator(IBookNumerable a)
    {
        aggregate = a;
    }
    public bool HasNext()
    {
        return index<aggregate.Count;
    }

    public Book Next()
    {
        return aggregate[index++];
    }
}

Интерфейс IBookIterator представляет итератор наподобие интерфейса IEnumerator. Роль интерфейса составного агрегата представляет тип IBookNumerable. Клиентом здесь является класс Reader, который использует итератор для обхода объекта библиотеки.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850