Iterator Pattern – Access Elements Without Exposing Structure

Explore how the Iterator Pattern enables sequential access to elements in a collection without exposing its internal structure. Includes real-world examples, UML, and C#/Python code.

🌀 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 JavaScript
  • for ... in and __iter__ in Python
But sometimes, these default approaches aren’t enough. When dealing with trees, graphs, or non-linear structures, implementing a custom iterator offers full control and adaptability.

🌳 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.