Design Patterns: Iterator Patterns

Posted by pleisar on Sat, 10 Aug 2019 14:20:43 +0200

Design Patterns (17): Iterator Patterns

Definition:

  • Provides a method to traverse an aggregated object sequentially, providing the required interfaces for traversing different aggregated structures without exposing the internal representation of the object.

The main solutions are:

  • Different ways to traverse the entire integration object

How to solve:

  • Instead of aggregating objects, the responsibility for wandering between elements is vested in the iterator.

Take code as an example
The class diagram is as follows

Definition of Aggregate classes

Aggregate defines an interface for aggregation that separates the client from the implementation of the object set

#include <iostream>
#include <stdexcept>
#include <vector>

class Iterator;
class ConcreteAggregate;

class Aggregate
{
public:
  virtual ~Aggregate() {}
  
  virtual Iterator *createIterator() = 0;
  // ...
};

Definition of ConcreteAggregate

Concrete Aggregate has a set of objects and implements a method for returning Iterator to its collection

class ConcreteAggregate : public Aggregate
{
public:
  ConcreteAggregate( const unsigned int size )
  {
    list = new int[size]();
    count = size;
  }
  
  ~ConcreteAggregate()
  {
    delete[] list;
  }
  
  Iterator *createIterator();
  
  unsigned int size() const
  {
    return count;
  }
  
  int at( unsigned int index )
  {
    return index;
  }
  // ...

private:
  int *list;
  unsigned int count;
  // ...
};

Definition of Iterator

Iterator provides the interfaces that all iterators must implement and a set of methods for traversing elements

class Iterator
{
public:
  virtual ~Iterator() { /* ... */ }
  
  virtual void first() = 0;
  virtual void next() = 0;
  virtual bool isDone() const = 0;
  virtual int currentItem() const = 0;
  // ...
};

Definition of ConcreteIterator

Concrete Iterator implements interfaces that manage the current location of iterators

class ConcreteIterator : public Iterator
{
public:
  ConcreteIterator( ConcreteAggregate *l ) :
    list( l ), index( 0 ) {}
  
  ~ConcreteIterator() {}
  
  void first()
  {
    index = 0;
  }
  
  void next()
  {
    index++;
  }
  
  bool isDone() const
  {
    return ( index >= list->size() );
  }
  
  int currentItem() const
  {
    if ( isDone() )
    {
      return -1;
    }
    return list->at(index);
  }
  // ...

private:
  ConcreteAggregate *list;
  unsigned int index;
  // ...
};


5. Call of main function

Iterator *ConcreteAggregate::createIterator()
{
  return new ConcreteIterator( this );
}


int main()
{
  unsigned int size = 5;
  ConcreteAggregate list = ConcreteAggregate( size );
  
  Iterator *it = list.createIterator();
  for ( ; !it->isDone(); it->next())
  {
    std::cout << "Item value: " << it->currentItem() << std::endl;
  }
  
  delete it;
  return 0;
}

Operation results:

Item value: 0
Item value: 1
Item value: 2
Item value: 3
Item value: 4

This simplifies the implementation of an iterator pattern

Advantages and disadvantages of iterator pattern

Advantage:

1. It supports traversing an aggregated object in different ways.

2. Iterators simplify aggregation classes.

3. There can be multiple traversals on the same aggregation.

4. In the iterator mode, it is convenient to add new aggregator classes and iterator classes without modifying the original code.

Disadvantages:
1. Because the iterator pattern separates the responsibility of storing and traversing data, adding new aggregation classes requires adding new iterator classes corresponding to increasing the number of classes in pairs, which increases the complexity of the system to a certain extent.

Matters needing attention:

  • The iterator pattern separates the traversal behavior of the object, which not only does not expose the internal structure, but also allows external code to transparently access the data inside the collection.