Iterator Pattern Container Iteration

The Iterator Pattern is a behavioral pattern that provides a way to access the elements of an aggregate object one at a time without exposing its internal representation. In this pattern, the container object contains zero or more iterator objects used for traversing the elements within it.

In the Iterator Pattern, the iterator object holds a reference to the container object and provides methods to inspect the contents of the container. The iterator can traverse through the elements in the container and perform operations on them during traversal. Essentially, this pattern offers a way to access individual elements without revealing the internal structure of the aggregate.

The implementation of the Iterator Pattern typically involves four key roles:

  1. Abstract Iterator Class (Iterator): Defines an interface for accessing elements within the collection, including methods such as next() and current().
  2. Concrete Iterator Class (Concrete Iterator): Implements the interface defined by the abstract iterator class to traverse specific collections.
  3. Abstract Container/Aggregate Class (Aggregate): Defines the interface of a container object that includes an operation like providing access to iterators.
  4. Concrete Aggregate Class: Provides concrete implementations for accessing elements, containing one or more instances of the Iterator objects used to iterate through them.

The core idea behind the Iterator Pattern is to provide a way to traverse individual elements within a collection without exposing its internal representation. This pattern is particularly suitable in scenarios where:

  1. You need to access each element individually but do not want to expose the specific implementation details of the container's structure (e.g., whether it uses an array, linked list, or hash map).
  2. The same collection needs to be traversed using multiple different algorithms.

The Iterator Pattern enhances system flexibility and extensibility by decoupling the aggregate object from its traversal algorithm. Furthermore, it can improve performance as various iteration strategies allow for optimizations tailored to specific data structures (e.g., random access vs. sequential scan). However, this pattern may increase code complexity due to the need to define additional iterator classes to support different iteration algorithms or operations on a single collection.

# Example implementation of an Abstract Iterator interface in Python-like pseudocode:

class Iterator:
    def __init__(self):
        self._current_element = None
    
    def has_next(self) -> bool:
        """Check if there are more elements to traverse."""
        return False  # Placeholder logic
    
    def next(self):
        """Return the current element and advance to the next one. Raises StopIteration when empty."""
        raise StopIteration("No more elements")

# Example implementation of a Concrete Iterator for a List:

class ListIterator(Iterator):
    def __init__(self, container_list):
        self.container = container_list
        self.index = 0
    
    def has_next(self) -> bool:
        return self.index < len(self.container)
    
    def next(self):
        if not self.has_next():
            raise StopIteration("No more elements")
        
        element = self.container[self.index]
        self.index += 1
        
        # Simulate operation on the container during traversal (e.g., logging or modification check)
        print(f"Processing element at index {self.index - 1}: {element}")
        
        return element

# Example implementation of an Abstract Aggregate interface:

class ContainerAggregate:
    def get_iterator(self):
        """Provide access to the iterator object."""
        pass
    
    # Other aggregate methods...

# Concrete example using a List as the container and creating its specific iterator:

my_list = [10, 20, 30]
list_iter = ListIterator(my_list)

while list_iter.has_next():
    current_item = list_iter.next()
    print(f"Current item: {current_item}")