Applied Design Patterns with Java
Behavioral :: Iterator (257) {C ch 19}
Intent
Provide a way to access the elements of an aggregate object sequentially
without exposing its underlying representation.
As Known As
Cursor
Motivation
An aggregate object such as a list should have
a way to access its elements without exposing its internal structure, and to traverse the list in different ways,
but without bloating the interface with operations for different traversals. The Iterator pattern allows
this. The key idea in this pattern is to take the responsibility
for access and traversal out of the list object and put it into an Iterator object. The
Iterator class defines an interface for accessing the list's elements. An Iterator
object is responsible for keeping track of the current element; that is, it knows which elements have been traversed
already. For example, a List class would call for a ListIterator with the following relationship between them:
Before instantiating ListIterator, supply the List instance to traverse,
then access the list's elements sequentially. The CurrentItem operation returns the current element in the list,
First initializes the current element to the first element, Next advances the current element to the next element,
and IsDone tests whether traversal has advanced beyond the last element.
Separating the traversal mechanism from the List object allows defining Iterators for different traversal policies
without enumerating them in the List interface. I.e.: FilteringListIterator might provide access only to those
elements that satisfy specific filtering constraints. Note that the Iterator and the list are coupled, and the client must know that it is a list that's traversed
as opposed to some other aggregate structure. Hence the client commits to a particular aggregate structure. It
would be better to change the aggregate class without changing client code, by generalizing the Iterator concept
to support polymorphic
iteration.
As an example, consider a SkipList implementation of a list. A skiplist is a probabilistic data structure with characteristics similar to balanced trees.
The code should work for both List and SkipList objects. Define an AbstractList class that provides a common interface
for manipulating lists. Define an abstract Iterator class for a common iteration interface. Define concrete Iterator subclasses
for the different list implementations. Thus, the iteration
mechanism becomes logically independent of concrete aggregate classes;
this is crucial to how the pattern works.
The remaining problem is how to create the Iterator. The code should be independent of the concrete List subclasses, so don't instantiate
a specific class. Instead, make the list objects responsible
for creating their corresponding Iterator. This requires an operation like CreateIterator through which clients request
an Iterator
object. CreateIterator is an example of a Factory
Method (107). It is used here to let a client ask a list object
for the appropriate Iterator, giving rise to two class hierarchies, one for lists and another for Iterators. The CreateIterator
Factory Method "connects"
the two hierarchies.