C++ Iterator Design Pattern Example

1. Definition

The Iterator Design Pattern provides a way to access the elements of a collection object in a sequential manner without exposing its underlying representation. It decouples the collection from the code that traverses its elements.

2. Problem Statement

How can one provide a uniform interface to traverse different collections (e.g., arrays, linked lists, trees)? You want to scan through a collection without knowing its internal structure, but doing so without a pattern can lead to messy code, tightly coupled with the collection's structure.

3. Solution

Introduce an iterator object that encapsulates the details of accessing and traversing the collection. The client code receives this iterator from the collection and uses its methods to traverse the collection without knowing its internal structure.

4. Real-World Use Cases

1. Navigating items in a playlist of a music player.

2. Traversing elements in various types of data structures, like trees, graphs, or arrays.

3. Paging through items in a database result set.

5. Implementation Steps

1. Define an Iterator interface with methods for accessing collection elements sequentially.

2. Implement concrete iterators for specific collection classes.

3. Make collection classes return concrete iterator instances when traversal is needed.

6. Implementation in C++

// Step 1: Iterator Interface
class Iterator {
public:
    virtual ~Iterator() {}
    virtual bool hasNext() = 0;
    virtual int next() = 0;
};
// Step 2: Concrete Iterator
class ArrayIterator : public Iterator {
private:
    int* array;
    int size;
    int index;
public:
    ArrayIterator(int* arr, int s) : array(arr), size(s), index(0) {}
    bool hasNext() override {
        return index < size;
    }
    int next() override {
        return array[index++];
    }
};
// Collection Class
class IntegerArray {
private:
    int* array;
    int size;
public:
    IntegerArray(int* arr, int s) : array(arr), size(s) {}
    Iterator* createIterator() {
        return new ArrayIterator(array, size);
    }
};
// Example Usage
int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    IntegerArray arr(numbers, 5);
    Iterator* it = arr.createIterator();
    while (it->hasNext()) {
        std::cout << it->next() << " ";
    }
    delete it;
    return 0;
}

Output:

1 2 3 4 5

Explanation:

1. Iterator is the abstract interface that defines the contract for iteration.

2. ArrayIterator is a concrete iterator for arrays.

3. IntegerArray is a collection class that returns a concrete iterator for its contained items.

In the main function, we define an array of integers and wrap it in the IntegerArray class. We then create an iterator for the array and use it to print each integer in the array.

7. When to use?

Use the Iterator pattern when:

1. You want to provide a uniform traversal interface for different collections.

2. You want to decouple the collection from its traversal mechanism.

3. Multiple traversal paths over a collection might be necessary.


Comments