Iterator Pattern
The Iterator is a behavioral design pattern that provides a way to access elements of a collection sequentially without exposing its underlying representation (e.g., array, list, tree). It decouples traversal logic from the collection, enabling multiple independent traversals and hiding internal structure.
Key Principles
- Encapsulation: Collection manages iterator creation; client only uses iterator.
- Single Responsibility: Collection focuses on storage; iterator on traversal.
- Polymorphism: Different iterators for same collection (e.g., forward/reverse).
Structure
- Iterator: Interface with methods like
next(),has_next(). - Concrete Iterator: Implements traversal for specific collection.
- Aggregate: Interface for creating iterator.
- Concrete Aggregate: Implements iterator factory method.
Python Example: Custom List Iterator
from typing import List, Any
# Iterator Interface
class Iterator:
def has_next(self) -> bool:
raise NotImplementedError
def next(self) -> Any:
raise NotImplementedError
# Concrete Iterator
class ListIterator(Iterator):
def __init__(self, collection: List[Any]):
self._collection = collection
self._index = 0
def has_next(self) -> bool:
return self._index < len(self._collection)
def next(self) -> Any:
if not self.has_next():
raise StopIteration
item = self._collection[self._index]
self._index += 1
return item
# Aggregate Interface
class IterableCollection:
def create_iterator(self) -> Iterator:
raise NotImplementedError
# Concrete Aggregate
class CustomList(IterableCollection):
def __init__(self):
self._items: List[Any] = []
def add(self, item: Any):
self._items.append(item)
def create_iterator(self) -> Iterator:
return ListIterator(self._items)
# Client Code
if __name__ == "__main__":
my_list = CustomList()
my_list.add("Apple")
my_list.add("Banana")
my_list.add("Cherry")
iterator = my_list.create_iterator()
while iterator.has_next():
print(f"Item: {iterator.next()}")
# Multiple independent iterators
iter1 = my_list.create_iterator()
iter2 = my_list.create_iterator()
print(f"First from iter1: {next(iter1)}") # Apple
print(f"First from iter2: {next(iter2)}") # Apple
Output:
Item: Apple
Item: Banana
Item: Cherry
First from iter1: Apple
First from iter2: Apple
When to Use Iterator
- Hide Internal Structure: Traverse trees, graphs without exposing nodes.
- Multiple Traversals: Independent iterators (e.g., two loops over same data).
- Uniform Access: Same interface for different collections.
- Avoid: Simple lists where built-in iteration suffices.
Summary: Iterator separates traversal from collection, providing a standard way to iterate over elements while keeping implementation details hidden. In Python, it's built-in via __iter__() and __next__(), but the pattern is explicit in custom structures.