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:
- Don’t need to know the actual data structure.
- Don’t need to get all of the elements at once, and we can keep track of where we are in the iteration.
- The provided elements can be infinite.
Class Diagram
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;
}