Iterator pattern

Definition

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

Introduction

As a Java programmer, it’s likely you have worked with aggregate objects, which are referred as collections in Java. A collection is an object that acts as a container to store other objects, which are referred as elements. Based on how a collection stores elements, there are different types of collections. For example, some collections may allow duplicate elements while others may not. ArrayList, LinkedList, HashMap, LinkedHashSet are few of the collections that are part of the Java Collection Framework. Like any other objects, a collection can be passed around the application or stored in other collection objects.

A collection at the minimum needs to provide clients methods to add and remove elements from it. But more importantly, it needs to allow clients to traverse through the elements it stores. A collection class can itself can implement the functionality required to provide access and allow traversal through its elements. But by doing so, the collection’s underlying structure and implementation will be exposed to clients. This is a bad object-oriented design principle that does not follow encapsulation. In addition, if you go back and revisit the SOLID design principles, it will be apparent that implementing element access and traversal operations in the collection itself is a clear violation of the Single Responsibility Principle. A collection should be only responsible to act as a container for storing elements, and any other responsibility such as element access and traversal should not be part of it.

The Iterator pattern addresses such recurring problems when dealing with aggregate objects. What this pattern says is that aggregate objects should provide a way to access its elements sequentially without exposing its internal structure. To accomplish the intent of the pattern, we need to separate the responsibility for access and traversal of the elements stored in the aggregate object and assign it to another object, which is referred as the iterator. The iterator keeps track of the elements and can perform different types of traversals sequentially based on what you want to accomplish. It is important to decouple the iterator object from the aggregate object so it can be reused for traversing other types of aggregate objects. You can design your program in such a way that the client asks the aggregate objects for its iterator, and the aggregate object returns the iterator initialized with the data structure holding the elements. The client then uses the iterator to traverse through the elements.

Participants in the pattern

  1. Aggregate (PatternAggregate): Is an interface that declares the methods to create and return an iterator.
  2. ConcreteAggregate (PatternAggregateImpl): Is a concrete class that implements the Aggregate interface to create and return an iterator.
  3. Iterator (PatternIterator): Is an interface with methods to allow clients to access and traverse elements.
  4. ConcreteIterator (PatternIteratorImpl): Is a concrete class that implements the Iterator interface. Objects of this class keeps track of the elements and implements access and traversal operations on the elements.

Reference

https://github.com/explorer436/programming-playground/tree/main/java-playground/design-pattern-samples/design-pattern-examples

  1. https://springframework.guru/gang-of-four-design-patterns/iterator-pattern/