Iterator Pattern

The Iterator Pattern is a behavioral design pattern that provides a way to access elements of a collection sequentially without exposing the underlying representation of the collection.

It allows you to iterate over the elements of an aggregate object (like a list or array) without knowing its internal structure.

Iterator Pattern Real-World Analogy:

Think of a music playlist as an analogy for the Iterator Pattern. You have a collection of songs (aggregate object), and you want to listen to each song one by one without knowing how the playlist is implemented or what type of data structure is used.

Iterator Pattern C++ Example:

Let’s understand the Iterator Pattern with a simple example of a collection of books. We’ll create an iterator to traverse the books without knowing the underlying collection’s implementation.

Iterator Pattern Explanation of Code:

#include <iostream>
#include <string>
#include <vector>

// Iterator Interface
class Iterator {
public:
    virtual bool hasNext() const = 0;
    virtual std::string next() = 0;
};

// Concrete Iterator: BookIterator
class BookIterator : public Iterator {
private:
    std::vector<std::string> books;
    int currentIndex;

public:
    BookIterator(const std::vector<std::string>& books) : books(books), currentIndex(0) {}

    bool hasNext() const override {
        return currentIndex < books.size();
    }

    std::string next() override {
        return books[currentIndex++];
    }
};

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

// Concrete Aggregate: BookCollection
class BookCollection : public Aggregate {
private:
    std::vector<std::string> books;

public:
    void addBook(const std::string& book) {
        books.push_back(book);
    }

    Iterator* createIterator() override {
        return new BookIterator(books);
    }
};

int main() {
    BookCollection bookCollection;
    bookCollection.addBook("Book 1");
    bookCollection.addBook("Book 2");
    bookCollection.addBook("Book 3");

    Iterator* iterator = bookCollection.createIterator();

    while (iterator->hasNext()) {
        std::cout << iterator->next() << std::endl;
    }

    delete iterator;

    return 0;
}

Output:

Book 1
Book 2
Book 3

Iterator Pattern Explanation of the Code:


In this example, we have four main components:

  1. Iterator is the iterator interface that declares methods like hasNext to check if there are more elements and next to retrieve the next element.
  2. BookIterator is the concrete iterator that implements the iterator interface for the BookCollection.
  3. Aggregate is the aggregate interface that declares a method createIterator to create an iterator object.
  4. BookCollection is the concrete aggregate that holds a collection of books and implements the createIterator method to create a BookIterator.

In the main function, we create a BookCollection and add some books to it. We then create an iterator for the collection using the createIterator method. Using the iterator, we can traverse through the books without knowing how they are stored inside the BookCollection.

Applications of Iterator Pattern:

  1. Collections and containers: It’s widely used in various programming languages to iterate over collections like arrays, lists, or maps.
  2. File parsing: Iterator pattern can be used to read and parse files with structured data (e.g., CSV files).

Iterator pattern Pros and Cons:

Iterator Pattern Pros:

  • Encapsulates iteration: It separates the iteration logic from the underlying collection, providing a clean and uniform way to access elements.
  • Supports multiple traversals: You can have multiple iterators for the same collection, each maintaining its independent iteration state.
  • Flexibility: You can easily change the iteration algorithm without affecting the client code.

Iterator Pattern Cons:

  • Complexity: Implementing the Iterator Pattern may introduce additional complexity, especially for small collections where direct iteration might be simpler.
  • Performance: For some simple collections, the Iterator Pattern might add overhead compared to direct iteration.
  • Extra memory: It may require additional memory to store iterator objects, depending on the implementation.