Iterator Design Pattern

Definition

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

Introduction

As usual we will start with an example. We have a Department interface which models the various departments in a supermarket. Let us say we have two implementations of it viz., BeveragesDepartment and VegetablesDepartment.

public interface Department {
    void addItem(Item item);
    Item getItem(String name);
}
public class BeveragesDepartment implements Department {
    private Map<String, Item> beverages = new HashMap<>();

    @Override
    public void addItem(Item item) {
        beverages.put(item.getName(), item);
    }

    @Override
    public Item getItem(String name) {
        return beverages.get(name);
    }

    public Map<String, Item> getAllItems() {
        return new HashMap<>(beverages);
    }
}
public class VegetablesDepartment implements Department {
    private Set<Item> vegetables;

    public VegetablesDepartment() {
        this.vegetables = new HashSet<>();
    }
    @Override
    public void addItem(Item item) {
        vegetables.add(item);
    }

    @Override
    public Item getItem(String name) {
        return vegetables.stream()
                .filter(vegetable -> vegetable.getName().equals(name))
                .findFirst()
                .orElse(null);
    }
    public Set<Item> getAllItems() {
        return new HashSet<>(vegetables);
    }
}
/**
 * An Item.
 */
public class Item {
    private String name;
    private double price;

    public Item(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Item other = (Item) obj;
        return this.name.equals(other.name);
    }

    @Override
    public String toString() {
        return name + ": $" + price;
    }
}

Each of the Department subclasses holds/encompasses a collection of Items. Each subclass uses a different data structure for holding the collection. The BeveragesDepartment uses a Map whereas the VegetableDepartment uses a Set to hold the items.The Department subclasses are called aggregates since it holds a collection of items.

Now, we have a client that wants to iterate all the items within a Department and print them. It looks like:

public class ItemDisplayer {
    public void displayAllItems(VegetablesDepartment vegetablesDepartment, 
                                BeveragesDepartment beveragesDepartment) {
        Set<Item> vegetables = vegetablesDepartment.getAllItems();
        vegetables.forEach(System.out::println);

        Map<String, Item> beverages = beveragesDepartment.getAllItems();
        beverages.values().forEach(System.out::println);
    }
}

The displayAllItems method takes in both the subclasses and gets the items from them and prints it. There are a few problems in this implementation

  1. The client (ItemDisplayer) has to know the actual type used by a Department to store the Items.
  2. By virtue of point, the Departments do not encapsulate its underlying data structure for storing the collection.

Hence, when we add a new Department, the client code has to change to enable iterating over the collection of Items present within the Department.

Encapsulating the iteration

We can use the design principle of encapsulating what varies. (We used the same design principle in the Strategy Design Pattern too). What is changing here is the logic for iteration. Hence, we want to encapsulate that.

Java Iterator

Java has an interface for Iterator. It looks like:

class Iterator<T> {
    boolean hasNext();
    T next();
}

The Iterator abstracts iterating over a collection or an aggregate. To iterate over a collection, we first have to get an Iterator object for that collection. The hasNext method must be called to know if there are more items in the aggregate to iterate through. If it returns true, call the next method to get the next object from the collection. Once all the elements are iterated upon and the collection has no items, calling hasNext will return false.

Java Iterable

Now, to make our aggregates (Departments) support clients to iterate over the items (Item), they need to have a method that returns an Iterator. Java already has an interface for this – it is the Iterable.

class Iterable<T> {
    Iterator<T> iterator();
}

It has a single method iterator, invoking which will return an Iterator instance. Then this iterator object can be used to iterate over the collection of items the aggregate holds. 

Note: If an Iterator is exhausted, it cannot be reused. A new Iterator instance has to be obtained by calling the iterator method.
Now let us apply this to our Departments.

Applying the Iterator Design Pattern

First, we will make our Department interface extend Iterable<Item>. Next, the subclasses will have to implement the iterator method that returns an Iterator. This Iterator will enable the client to iterate over the individual Items the aggregate encapsulates. Let us make these changes

public interface Department extends Iterable<Item>
public class VegetablesDepartment implements Department {
    //rest of the code
    
    @Override
    public Iterator<Item> iterator() {
        return vegetables.iterator();
    }
}
public class BeveragesDepartment implements Department {
    //rest of the code
    @Override
    public Iterator<Item> iterator() {
        return beverages.values().iterator();
    }
}

Luckily, the collection we use (Map and Set) already supports obtaining an Iterator for it. Say, you are using an array, then you have to implement your own Iterator to iterate over the items in the array.

Let us now fix the client – ItemDisplayer. Now, the ItemDisplayer no longer needs to know the actual underlying aggregate type. So, we can just pass a List<Department> to it. It can operate using the Iterable contract. In other words, treat each Department as an Iterable and obtain the Iterator to iterate over the elements.

public class ItemDisplayer {
    public void displayAllItems(List<Department> departments) {
        departments
                .forEach(this::printItems);

    }
    private void printItems(Department department) {
        for (Item item : department) {
            System.out.println(item);
        }
    }
}

Have you noted that we are able to use the enhanced-for loop to iterate here? Yes, we can use any class that implements Iterable in a for-each loop.

Summary

Let us have a quick recap of what we have done 

Old implementationIterator pattern implementation
The Department was not well encapsulated. The client has to know the collection type to iterate.The Departments are well encapsulated. Hence, the client
does not know what the actual underlying collection type is.
We needed two loops to iterate over Items in
two Departments.
We handle any number of Department polymorphically.
The client is tied to the implementation of the
underlying collection (Map and Set).
Client now uses the Iterator interface.
The client has to be passed separate
Department types.
Now, the client only relies on the (common) Department
interface (which implements Iterable).

This implies that if a new Department subclass is added, the ItemDisplayer class need not change. We can pass the new Department in the list of Departments passed and it will iterate and print the Items.

Structure

Participants

Iterator
  •    Defines an interface for accessing and traversing elements.
ConcreteIterator
  • Implements the Iterator interface.
  • Keeps track of the current position in the traversal of the aggregate.
Aggregate (Department)
  •   Defines an interface for creating an Iterator object.
ConcreteAggregate (Department subclasses)
  •  Implements the Iterator creation interface to return an instance of the proper ConcreteIterator
The Client is the ItemDisplayer.
 

Design Principles used

Conclusion

We started with the code where the aggregates exposed its internals to the client. The client uses the concrete collection type used to iterate over the elements. Then, using the Iterator design pattern, we refactored the aggregate to return an iterator. The client then used this Iterator to iterate over the elements.

Check out on my other design pattern posts

  1. Strategy Design Pattern
  2. Factory Method Pattern
  3. Abstract Factory Pattern

Leave a Reply