Design patterns: Iterator

Mauto 3 min

The Iterator Pattern is a behavioral design pattern that provides a way to access the elements of a collection sequentially without exposing its internal representation. This pattern separates the iteration logic from the collection, allowing you to traverse a data structure transparently and flexibly, regardless of its underlying implementation.

Strengths

  1. Separation of Concerns: The Iterator Pattern separates the code that manages the data structure from the code that traverses the collection. This simplifies maintenance and improves code readability.
  2. Uniform Access: It provides a uniform interface for traversing different data structures, making client code independent of the collection’s internal representation.
  3. Easy to Swap the Data Structure: The pattern makes it easy to change the underlying data structure (array, list, set, etc.) without having to modify the client code that uses the iterator.
  4. Flexibility and Reuse: Adding or changing an iterator’s behavior (for example, for filtering or reverse iteration) is simple and requires no changes to the original collection.
  5. Compatibility with the Single Responsibility Principle: With the Iterator Pattern, the iteration logic is delegated to a dedicated iterator, letting the collection focus on managing data and improving the code’s design.

Use Case: A Book Collection

Imagine we have a collection of books and want to be able to iterate over it without exposing the internal details of its implementation.

PHP Code

// Defines the interface for the iterator
interface Iterator {
    public function hasNext(): bool;
    public function next();
}

// Book collection
class BookCollection {
    private $books = [];

    public function addBook($book) {
        $this->books[] = $book;
    }

    public function createIterator(): Iterator {
        return new BookIterator($this->books);
    }
}

// Iterator implementation for the book collection
class BookIterator implements Iterator {
    private $books;
    private $position = 0;

    public function __construct($books) {
        $this->books = $books;
    }

    public function hasNext(): bool {
        return $this->position < count($this->books);
    }

    public function next() {
        return $this->books[$this->position++];
    }
}

// Use case
$collection = new BookCollection();
$collection->addBook("The Catcher in the Rye");
$collection->addBook("To Kill a Mockingbird");
$collection->addBook("1984");

// Using the iterator to traverse the collection
$iterator = $collection->createIterator();
while ($iterator->hasNext()) {
    echo $iterator->next() . PHP_EOL;
}

Explanation

  • Iterator interface: Defines the standard methods an iterator must implement (hasNext() and next()).
  • BookCollection class: Holds the book collection and a createIterator method that returns an iterator.
  • BookIterator class: Implements the Iterator interface and provides the mechanism for traversing the book collection.
  • Usage: We create a book collection, add a few books, and use the iterator to iterate over the collection’s elements without directly accessing its internal structure.

Conclusion

The Iterator Pattern offers a flexible way to traverse complex collections, improving code maintainability and separation of concerns. It is ideal when the data structure may change or when you want to hide the traversal logic from client code.

BACK TO THE GUIDE

Copiato