printПаттерны поведения

printИтератор

Iterator

Назначение
Предоставляет способ последовательного доступа ко всем элементам составного объекта, не раскрывая его внутреннего представления.

Результаты
1. Сложные составные объекты можно обходить по-разному. Для изменения алгоритма обхода нужно определить подкласс класса Iterator и заменить один экземпляр итератора другим.
2. Итераторы упрощают интерфейс класса Aggregate.
3. Одновременно для данного агрегата может быть активно несколько обходов.
4. Модификация составного объекта в то время, как совершается его обход, может оказаться опасной. Для создания устойчивого к модификациям итератора необходимо изменять состояние всех созданных итераторов при вставке или удалении.
5. Если итерацией управляет клиент, то итератор называется внешним, в противном случае – внутренним. В случае внутреннего итератора клиент передает составному объекту некоторую операцию, а объект сам применяет эту операцию к каждому элементу. Внешние итераторы обладают большей гибкостью, чем внутренние. Например, сравнить две коллекции на равенство с помощью внешнего итератора очень легко, а с помощью внутреннего – практически невозможно.

Структура
14682.png

Реализация
// Составной объект
class Aggregate {
public:
  virtual Iterator *CreateIterator()=0;
};
// Элементы составного объекта
class Item;
// Конкретный объект реализует интерфейс создания итератора
class Aggregate1 : public Aggregate {
  friend class Iterator1;
  Item *items;
  int size;
public:
  Iterator1 *CreateIterator() { return new Iterator1(this); }
};
// Итератор определяет интерфейс для доступа и обхода элементов
class Iterator {
public:
  virtual void First()=0;
  virtual void Next()=0;
  virtual bool IsDone()=0;
  virtual Item &CurrentItem()=0;
};
// Конкретный итератор реализует интерфейс класса Iterator
class Iterator1 {
  friend class Aggregate1;
  Aggregate1 *a;
  int i;
  Iterator1(Aggregate1 *);
public:
  void First() { i=0; }
  void Next() { ++i; }
  bool IsDone() { return i>=a->size; }
  Item &CurrentItem() { return a->items[i]; }
};
// Использование
Aggregate *a;
Iterator *it=a->CreateIterator();
for(it->First();!it->IsDone();it->Next())
  ... действия с it->CurrentItem() ...
В STL для работы с итераторами используется перегрузка операций: для получения текущего элемента – операция разъадресации (*), для перехода на следующий элемент – операция инкремента (++), для проверки завершения – сравнение с со специальным итератором контейнера end().

Внутренний итератор (см. также паттерн посетитель)
// Элементы составного объекта
typedef int Item;
// Внутренний итератор
class Iterator {
public:
  virtual void Operation(Item&)=0;
};
// Составной объект
class Aggregate {
public:
  virtual void ApplyIterator(Iterator &)=0;
};
// Конкретный объект реализует интерфейс создания итератора
class Aggregate1 : public Aggregate {
  Item *items;
  int size;
public:
  void ApplyIterator(Iterator &)
  { for(int i=0;i<size;++i)
      Iterator.Operation(items[i]);
  }
};
// Использование
class SumIterator : public Iterator {
  int sum;
public:
  SumIterator():sum(0) {}
  void Operation(Item &it) { sum+=it; }
  int getSum() const { return sum; }
} sumIt;
...
Aggregate *a;
a->ApplyIterator(sumIt);
cout<<sumIt->getSum();
loading