Iterator Pattern

When programming, one gets to deal with all sorts of information representations. You have to make sure that information is accessible, accurate, and easy to interact with. It is important to provide proper channels of handling information representation, be it array or collection, or a single variable. In order to properly do that we often use different data structures and design patterns to create flawless and uniform flow.

Sometimes you can get different data representation for the same purpose. Say you have a list of books in the library that is represented with the use of simple array. Quite quick and easy to access, a little difficult to search. And now we need to develop an application that would display information about all available books in this town. That would mean that not only library’s books should be in that list, but also bookstore’s collection, which can be represented in any other way. The problem we would face now, would be that it is not easy for us to traverse both collections and to work with them uniformly. It would require us to rewrite the display() function every time we receive different traversable information unit. Luckily, there is an Iterator Pattern that helps us to deal with exactly this kind of problem.


The Iterator Pattern

provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.


The core idea of this pattern is that it enables classes to inherit the Iterator interface, and implement their own approach to accessing the data. Once you have that, you can implement iterators for all kinds of objects. We won’t have any problem adapting to various aggregate objects. Here are a couple things that happen here:

  • Aggregate object representations get encapsulated. The caller has no idea how each one of those is structured or implements access.
  • Now we can go ahead and put all of those iterators under one loop, and use polymorphism to access them uniformly.
  • We have methods such as hasNext(), next(), and remove(), (and sometimes a couple of others) that allow us all the necessary tools for iteration handling.
<?php
// Source: https://en.wikipedia.org/wiki/Iterator_pattern
// BookIterator.php

namespace DesignPatterns;

class BookIterator implements \Iterator
{
    private $i_position = 0;
    private $booksCollection;

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

    public function current()
    {
        return $this->booksCollection->getTitle($this->i_position);
    }

    public function key()
    {
        return $this->i_position;
    }

    public function next()
    {
        $this->i_position++;
    }

    public function rewind()
    {
        $this->i_position = 0;
    }

    public function valid()
    {
        return !is_null($this->booksCollection->getTitle($this->i_position));
    }
}

// BookCollection.php

namespace DesignPatterns;

class BookCollection implements \IteratorAggregate
{
    private $a_titles = array();

    public function getIterator()
    {
        return new BookIterator($this);
    }

    public function addTitle($string)
    {
        $this->a_titles[] = $string;
    }

    public function getTitle($key)
    {
        if (isset($this->a_titles[$key])) {
            return $this->a_titles[$key];
        }
        return null;
    }

    public function is_empty()
    {
        return empty($this->$a_titles);
    }
}

// index.php

require 'vendor/autoload.php';
use DesignPatterns\BookCollection;

$booksCollection = new BookCollection();
$booksCollection->addTitle('Design Patterns');
$booksCollection->addTitle('PHP7 is the best');
$booksCollection->addTitle('Laravel Rules');
$booksCollection->addTitle('DHH Rules');

foreach($booksCollection as $book){
    var_dump($book);
}

Using Iterator Pattern gives us a lot flexiblity in traversing objects. We do not need to know or expose any extra information to the caller, which also makes iterators reusable and interchangeable. Responsibility of traversal lie completely on the Iterator, which supports Single Responsibility Principle, and frees the object itself of the burden. Following this principle allows the class to stay simple and clear about what it does. If we ever need to make a change, we know where to go to do that.

2 thoughts on “Iterator Pattern

Leave a comment