Home [Design Patterns] Iterator Pattern
Post
Cancel

[Design Patterns] Iterator Pattern

Definition

The iterator pattern provides a way to access the elements of an aggregated objects in a sequential way without exposing the underlying representation.

Scenario

We want to go through a group of objects, but I don’t want to know what’s inside the group or what’s its structure is. I just need them to tell me how to go through the group elements, so that I can treat each group uniformly without knowing its detailed structure.

The benefit of this is:

  1. Don’t need to know the actual data structure.
  2. Don’t need to get all of the elements at once, and we can keep track of where we are in the iteration.
  3. The provided elements can be infinite.

Class Diagram

Iterator Pattern Iterator Pattern

We want to treat all iterables uniformly, so we define an interface IIterable, which contains GetIterator method. Every iterable oject (it can be a collection of anything) will inherit IIterable and return their own iterate patterns for their underlying structures. This is like the factory method pattern, but here, we create and return the iterators.

The iterator (IIterator) pattern here follows CurrentItem to get the current object pointed by the arrow, Next to move the arrow to the next object, IsDone to indicate it is done.

Used Cases

  • Iterating a group of objects.

Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// iterator_pattern.h
#ifndef __ITERATOR_PATTERN_H__
#define __ITERATOR_PATTERN_H__

#include <memory>
#include <string>

template<typename T>
class IIterator {
public:
    virtual bool IsDone() = 0;
    virtual void Next() = 0;
    virtual T CurrentItem() = 0;
};

template<typename T>
class IIterable {
    virtual std::unique_ptr<IIterator<T>> GetIterator() = 0;
};

class HandHeldInventory : public IIterable<std::string> {
private:
    // The item it returns, which is a string here, can be something else.
    std::string right_hand_;
    std::string left_hand_;

public:
    HandHeldInventory(const std::string& right_hand, const std::string& left_hand)
        : right_hand_(right_hand), left_hand_(left_hand) {}
    const std::string& GetRightHand() const { return right_hand_; }
    const std::string& GetLeftHand() const { return left_hand_; }
    std::unique_ptr<IIterator<std::string>> GetIterator() override;
};

class HandHeldInventoryIterator : public IIterator<std::string> {
private:
    HandHeldInventory* hand_held_inventory_;
    int current_index_ = 0;

public:
    explicit HandHeldInventoryIterator(HandHeldInventory* inventory) : hand_held_inventory_(inventory) {}
    bool IsDone() override;
    void Next() override;
    std::string CurrentItem() override;
};

#endif  //__ITERATOR_PATTERN_H__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// iterator_pattern.cpp
#include "iterator_pattern.h"

bool HandHeldInventoryIterator::IsDone() {
    return current_index_ >= 2;
}

void HandHeldInventoryIterator::Next() {
    current_index_++;
}

std::string HandHeldInventoryIterator::CurrentItem() {
    switch (current_index_) {
        case 0:
            return hand_held_inventory_->GetRightHand();
        case 1:
            return hand_held_inventory_->GetLeftHand();
        default:
            return "(null)";
    }
}

std::unique_ptr<IIterator<std::string>> HandHeldInventory::GetIterator() {
    return std::make_unique<HandHeldInventoryIterator>(this);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// main.cpp
#include <iostream>
#include "iterator_pattern.h"

int main() {

    HandHeldInventory inventory = HandHeldInventory("sword", "shield");
    std::unique_ptr<IIterator<std::string>> iterator = inventory.GetIterator();
    while (!iterator->IsDone()) {
        std::cout << iterator->CurrentItem() << std::endl;
        iterator->Next();
    }

    return 0;
}

Reference

This post is licensed under CC BY 4.0 by the author.