This is the 22nd post in a series on design patterns.
Iterator is a behavioral design pattern that lets you traverse elements of a collection without exposing its underlying representation (list, stack, tree, etc.)
Iterator Design Pattern
This pattern extracts the traversal behavior of a collection into a separate object called an iterator. A traversal object encapsulates not only the algorithm itself but all of the details of the traversal, such as how many elements are left to traverse. Consequently, several iterators can process the same collection simultaneously, and independently.
Iterators provide a primary way of retrieving elements of a collection. The client can run this method until it does not return anything, which means that it has traversed all the elements.
Iterators must implement the same interface. This makes the client code compatible with any collection type or any traversal algorithm as long as there’s a proper iterator.
UML Class Diagram
Not familiar with UML Class Diagram? I have written a detailed post on the UML Class diagram.
- Create the iterator interface. It must have a method for fetching the next element from a collection.
- Define the collection interface and describe a method for getting iterators. The return type should correspond to that of the iterator interface.
- Create your collection classes by implementing the collection interface. The main idea is to provide the client with a shortcut for creating iterators, tailored to a particular collection class. The collection object must be passed to the iterator’s constructor to establish a link between them.
- Replace all collection traversal codes with iterators in the client code. Each time the client needs to iterate over collection elements, it fetches a new iterator object.
Source Code Implementation
The Iterator interface declares the operations required to traverse a collection, fetch the next element, retrieve the current position, restart iterations, etc.
NotificationIterator implements specific algorithms for traversing collections. Iterators should track their traversal progress independently. Multiple iterators can traverse the same collection independently.
The Collection interface declares one or more methods for getting iterators that are compatible with the collection. For concrete collections to return various kinds of iterators, the return type of the methods must be declared as the iterator interface.
Each time the client requests an iterator, the NotificationCollection createIterator method returns an instance of the requested concrete iterator class.
Through its interfaces, the IteratorClient works with both collections and iterators. So, the client isn’t tied to concrete classes, allowing you to use multiple collections and iterators with the same client code.
When To Apply Iterator Design Pattern
- You should use the Iterator pattern when your collection has a complex under-the-hood data structure, but you want to hide its complexity from clients.
- Utilize the pattern to reduce duplication of traversal code across your application.
- If you want your code to be able to traverse different data structures or if the types of these structures are unknown beforehand, then you should use the Iterator pattern.
Pros of Iterator Design Pattern
- By separating bulky traversal algorithms into separate classes, you can simplify client code and collections.
- Implementing new types of collections and iterators without breaking existing code is possible.
- Each iterator object contains its own iteration state, so you can iterate over the same collection in parallel.