Wednesday, February 9, 2011

Behavioral Patterns - Iterator Pattern

Definition

Provide a way to move through a list of collection or aggregated objects without knowing its internal representations. 

Intent

  • Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
  • The C++ and Java standard library abstraction that makes it possible to decouple collection classes and algorithms.
  • Promote to “full object status” the traversal of a collection.
  • Polymorphic traversal

Problem

Need to “abstract” the traversal of wildly different data structures so that algorithms can be defined that are capable of interfacing with each transparently.

Where to use & benefits


Motivation

One of the most common data structures in software development is what is generic called a collection. A collection is just a grouping of some objects. They can have the same type or they can be all cast to a base type like object. A collection can be a list, an array, a tree and the examples can continue.

But what is more important is that a collection should provide a way to access its elements without exposing its internal structure. We should have a mechanism to traverse in the same way a list or an array. It doesn't matter how they are internally represented.

The idea of the iterator pattern is to take the responsibility of accessing and passing trough the objects of the collection and put it in the iterator object. The iterator object will maintain the state of the iteration, keeping track of the current item and having a way of identifying what elements are next to be iterated.

Intent

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

The abstraction provided by the iterator pattern allows you to modify the collection implementation without making any changes outside of collection. It enables you to create a general purpose GUI component that will be able to iterate through any collection of the application.

Implementation

Iterator Implementation - UML Class Diagram




The Iterator pattern is one, which allows you to navigate through a collection of data using a common interface without knowing about the underlying implementation.
Iterator should be implemented as an interface. This allows the user to implement it anyway its easier for him/her to return data.
We use iterators quite frequently in everyday life. For example, remote control of TV. Any remote control we use, either at home/hotel or at a friend’s place, we just pick up the TV remote control and start pressing Up and Down or Forward and Back keys to iterate through the channels.
What sort of interface can Iterator be in case of Remote Controls?
/**
* Iterator interface has method declarations for iterating through
* TV channels. All remote controls implement Iterator.
*/
public interface Iterator {
  public Channel nextChannel(int currentChannel);
public Channel prevChannel(int currentChannel);
}// End of interface


The channel iterator is common for all the remote controls. It’s like a specification implemented by all the remote control manufacturing companies.


/**
* ChannelSurfer is a part of remote control which implements the Iterator
* interface. This class overrides the nextChannel and prevChannel methods.
*/

public ChannelSurfer implements Iterator {
  /**
* nextChannel – method which takes the current channel number
* and returns the next channel.
*/
public Channel nextChannel (int currentChannel) {
    Channel channel = new Channel(currentChannel+1);
return channel;
   }    
  /**
* prevChannel – method which takes the current channel number
* and returns the previous channel.
*/
public Channel prevChannel (int currentChannel) {
      Channel channel = new Channel(currentChannel-1);
return channel;
  }  
}// End of class


/**
* RemoteControl class is the actual remote control and it behaves and makes
* use of ChannelSurfer.
*/
public class RemoteControl {
  private ChannelSurfer surfer;
private Settings settings;
public RemoteControl() {
    surfer = new ChannelSurfer();
settings = new Settings();
   }  
  /**
* getProgram returns the program for that channel.
*
*/
public getProgram(ChannelSurfer surfer) {
    return new Program(surfer.nextChannel());
   }    
}// End of class

We all know that every channel is associated to a program and it’s basically the program and not the channel number which a user wants to see. And so, the implementation which returns a program for channels surfed.

This tells us that we can apply some logic before returning the elements through iterator. We can set rules. The Iterator here, can also be programmed to return the ‘programs’ straight away rather than returning the channels.
The common Java iterator is Enumeration which has implicit
hasMoreElements()
and nextElement() methods.

The benefits of Iterator are about their strength to provide a common interface for iterating through collections without bothering about underlying implementation.



Iterator in Java: Before and after

Why read if you can watch?

Watch design patterns video tutorial
Read full article

Before

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

After

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 m_it;
        private int m_current;
        public Iterator(SomeClassWithData in)
        {
            m_collection = in;
        }
        public void first()
        {
            m_it = m_collection.m_data.iterator();
            next();
        }
        public void next()
        {
            try
            {
                m_current = m_it.next();
            }
            catch (NoSuchElementException ex)
            {
                m_current =  - 999;
            }
        }
        public boolean is_done()
        {
            return m_current ==  - 999;
        }
        public int current_item()
        {
            return m_current;
        }
    }

    public void add(int in)
    {
        m_data.add(in);
    }
    public Iterator create_iterator()
    {
        return new Iterator(this);
    }
}

class IteratorDemo
{
    public static void main(String[] args)
    {
        SomeClassWithData some_object = new SomeClassWithData();
        for (int i = 9; i > 0; --i)
          some_object.add(i);

        // get_data() has been removed.
        // Client has to use Iterator.
        SomeClassWithData.Iterator it1 = some_object.create_iterator();
        SomeClassWithData.Iterator it2 = some_object.create_iterator();

        for (it1.first(); !it1.is_done(); it1.next())
          System.out.print(it1.current_item() + "  ");
        System.out.println();

        // Two simultaneous iterations
        for (it1.first(), it2.first(); !it1.is_done(); it1.next(), it2.next())
          System.out.print(it1.current_item() + " " + it2.current_item() + "  ")
            ;
        System.out.println();
    }
}
1 2 3 4 5 6 7 8 9 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9

No comments:

Post a Comment