🌀 The Iterator Pattern: Seamless Traversal without Exposure
The Iterator Pattern is a core behavioral design pattern that enables sequential access to the elements of a collection—without revealing its internal structure. Rather than tying client code to how a collection stores its data, the pattern abstracts away the traversal logic. This allows the client to focus solely on what to do with each element, not how to get to the next one.
📺 Real-world Analogy
Think of using a TV remote: you press “Next” or “Previous” to browse channels. You don’t need to know how those channels are stored or indexed—just that each press moves you forward or backward. That’s iteration in action, without exposure.
🧠 Why Use the Iterator Pattern?
-
🔍 Encapsulation:
The iterator hides the internal structure of the collection, so you can change how data is stored without affecting client code.
// Client doesn't know if _names is a List, Array, etc. IIterator<string> iterator = nameCollection.CreateIterator(); while (iterator.HasNext()) { Console.WriteLine(iterator.Next()); }
-
🔁 Decouples traversal from collection:
Client code can iterate over a collection without knowing its implementation details.
// Traversal logic is in the iterator, not in the collection IIterator<string> iterator = nameCollection.CreateIterator(); while (iterator.HasNext()) { var name = iterator.Next(); // Do something with name }
-
🔄 Flexibility:
You can add support for different iteration modes (reverse, filtering, etc.) by creating new iterator classes.
// Example: Reverse iterator class ReverseNameIterator : IIterator<string> { private readonly List<string> _names; private int _position; public ReverseNameIterator(List<string> names) { _names = names; _position = _names.Count - 1; } public bool HasNext() => _position >= 0; public string Next() => _names[_position--]; }
-
👥 Multiple iterators at once:
You can have several iterators on the same collection, each with its own state.
// Two iterators traversing independently var iterator1 = nameCollection.CreateIterator(); var iterator2 = nameCollection.CreateIterator(); Console.WriteLine(iterator1.Next()); // Alice Console.WriteLine(iterator2.Next()); // Alice Console.WriteLine(iterator1.Next()); // Bob
💡 In Practice
Most modern programming languages provide built-in iterator mechanisms:
foreach
in C#for...of
in JavaScriptfor ... in
and__iter__
in Python
🌳 When You'll Want to Implement It Yourself
- Traversing non-linear structures like DOM trees or graphs
- Supporting custom ordering or element filtering
- Maintaining stateful iteration across async or paged data sources
Problem it Solves
- Collections often have complex internal representations.
- Client code may be tightly coupled with traversal logic.
- Difficult to implement multiple traversal strategies (e.g., forward, reverse, filtered).
Intent
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
Structure
classDiagram
class Iterator {
+hasNext()
+next()
}
class ConcreteIterator {
-collection
-position
+hasNext()
+next()
}
class Aggregate {
+createIterator()
}
class ConcreteAggregate {
-items
+createIterator()
}
Aggregate <|-- ConcreteAggregate
Iterator <|-- ConcreteIterator
ConcreteAggregate --> ConcreteIterator
When to Use
- When you need to access elements of a collection without exposing its internal details.
- When you want multiple or customizable traversal strategies.
💻 Code Examples
// C# Iterator Pattern Example
interface IIterator
{
bool HasNext();
T Next();
}
class NameIterator : IIterator
{
private readonly List _names;
private int _position = 0;
public NameIterator(List names)
{
_names = names;
}
public bool HasNext() => _position < _names.Count;
public string Next() => _names[_position++];
}
class NameCollection
{
private List _names = new List { "Alice", "Bob", "Charlie" };
public IIterator CreateIterator() => new NameIterator(_names);
}
# Python Iterator Pattern Example
class NameIterator:
def __init__(self, names):
self._names = names
self._index = 0
def __iter__(self):
return self
def __next__(self):
if self._index < len(self._names):
result = self._names[self._index]
self._index += 1
return result
else:
raise StopIteration()
names = ["Alice", "Bob", "Charlie"]
iterator = NameIterator(names)
for name in iterator:
print(name)
Pros and Cons
- ✅ Pros: Encapsulates traversal logic, supports multiple iterators, improves code readability
- ⚠️ Cons: Adds extra classes, external iterators can be more verbose
Real-World Use Cases
- Database cursor iteration
- Pagination over APIs
- Custom UI components navigating tab groups or menu items
🧠 Test your Understnainding
🔚 Conclusion
The Iterator Pattern helps build clean, maintainable code by decoupling the collection structure from the traversal mechanism. Whether you're writing your own data structures or working with APIs, knowing how iterators work gives you an elegant way to control access to elements.