The Iterator pattern lets you do all this. The key idea is to take the responsibility for access and traversal out of the aggregate object and put it into an Iterator object that defines a standard traversal protocol.
The Iterator abstraction is fundamental to an emerging technology called "generic programming". This strategy seeks to explicitly separate the notion of "algorithm" from that of "data structure". The motivation is to: promote component-based development, boost productivity, and reduce configuration management.
As an example, if you wanted to support four data structures (array, binary tree, linked list, and hash table) and three algorithms (sort, find, and merge), a traditional approach would require four times three permutations to develop and maintain. Whereas, a generic programming approach would only require four plus three configuration items.
The Client uses the Collection class’ public interface directly. But access to the Collection’s elements is encapsulated behind the additional level of abstraction called Iterator. Each Collection derived class knows which Iterator derived class to create and return. After that, the Client relies on the interface defined in the Iterator base class.
On early television sets, a dial was used to change channels. When channel surfing, the viewer was required to move the dial through each channel position, regardless of whether or not that channel had reception. On modern television sets, a next and previous button are used. When the viewer selects the "next" button, the next tuned channel will be displayed. Consider watching television in a hotel room in a strange city. When surfing through channels, the channel number is not important, but the programming is. If the programming on one channel is not of interest, the viewer can request the next channel, without knowing its number.
create_iterator()
method to the "collection" class,
and grant the "iterator" class privileged access.
first()
, is_done()
,
next()
, and current_item()
protocol to access
the elements of the collection class.
Before | After | |
---|---|---|
// The class SomeClassWithData provides access // to its internal data structure. Clients can // accidently or maliciously trash that data // structure. class SomeClassWithData { private TreeSet<Integer> m_data = new TreeSet<Integer>(); public void add( int in ) { m_data.add( in ); } public Collection get_data() { return m_data; } } class IteratorDemo { public static void main( String[] args ) { SomeClassWithData some_object = new SomeClassWithData(); for (int i=9; i > 0; --i) some_object.add( i ); Collection data = some_object.get_data(); for (java.util.Iterator it = data.iterator(); it.hasNext(); ) System.out.print( it.next() + " " ); System.out.println(); // Do we really want a client to be able to // trash encapsulated state? data.clear(); data = some_object.get_data(); System.out.println( "size of data is " + data.size() ); } } // 1 2 3 4 5 6 7 8 9 // size of data is 0 | // Take traversal-of-a-collection functionality out // of the collection and promote it to "full object // status". This simplifies the collection, allows // many traversals to be active simultaneously, and // decouples collection algorithms from collection // data structures. class SomeClassWithData { private TreeSet<Integer> m_data = new TreeSet<Integer>(); public class Iterator { private SomeClassWithData m_collection; private java.util.Iterator |
Iterator can traverse a Composite. Visitor can apply an operation over a Composite. [GoF, p173]
Polymorphic Iterators rely on Factory Methods to instantiate the appropriate Iterator subclass. [GoF, p271]
Memento is often used in conjunction with Iterator. An Iterator can use a Memento to capture the state of an iteration. The Iterator stores the Memento internally. [GoF, p271]