Java Tip 38: The trick to "Iterator Observers"

Factor out common code and make your Iterators observable

On this month's tip we will take a brief look at the Iterator and Observer design patterns and then go on to examine how to use these patterns together to create Iterator Observers. Readers familiar with these two patterns may want to move straight to the section called Iterator Observers.

Iterators

Iterators provide an abstract mechanism to access elements in collection classes without revealing their underlying representations. A collection class is a class that can contain a number of items in a certain data structure such as linked lists and hash tables. An example of a collection class is java.util.Vector.

The first two listings explore why hiding the underlying representation of a collection class helps reduce the level of coupling between classes. The following enumeration is an example of an iterator in Java:

public  interface  java.util.Enumeration
{    public abstract boolean hasMoreElements();    public abstract Object nextElement();
}

If used correctly, Iterators promote loose coupling between collection classes and their clients. Listing 1 illustrates a poor use of an Iterator.

public class Display extends Panel
{
    private List itemList= new List();
    public Display()
    {
        add( itemList );
    }
    public void InitList( Vector opts )
    {
        Enumeration e=opts.elements();
        while( e.hasMoreElements()  )   
        {
            String item=(String)e.nextElement();
            itemList.addItem( item );
        }
    }
}

Listing 1: A poor use of an Iterator

So why have I said that the approach used in Listing 1 is poor? InitList() is coupled to the use of a Vector. Consider the impact and effort involved if your system contains many dependencies on a particular collection class -- in this case Vector -- and you later need to change it to your own class or a third-party class that suits your system better. So how can we improve this design? Listing 2 shows how, by using polymorphism and passing an iterator as a parameter rather than a Vector, class Display can be used by any client with a collection class that supports the Enumeration interface. Class Test demonstrates this by passing either a Vector or a hashtable's Enumeration to the Display object. (For more information on abstraction and polymorphism, see my JavaWorld article, "Polymorphism and Java.")

public class Display extends Panel
{
    //as listing 1
    public void InitList( Enumeration e )
    {
        while( e.hasMoreElements()  )   
        {
            String item=(String)e.nextElement();
            itemList.addItem( item );
        }
    }
}
public class Test
{
    private Vector      v   = new Vector();
    private Hashtable   ht   = new Hashtable();
    private Display         disp      = new Display();
    public void WithVector()
    {
        disp.InitList( v.elements() );
    }
    public void WithHashTable()
    {
        disp.InitList( ht.elements() );
    }
}

Listing 2: Improved usage of Iterator

Those readers who are interested in experimenting with Iterators should write a class that can read a row at a time from a text file, or even better from a database table, and provides an iterator over the rows in the form of the java.util.Enumeration interface. An example using JDBC occurs later in this article.

Observers

Examples of the Observer pattern can be found in Smalltalk's MVC (Model View Controller), Microsoft's C++ MFC document/view architecture, and Java's own Observer and Observable interfaces. The Observer pattern generally is used to inform clients (Observers) if the state of an object (known as the subject) that they have expressed an interest in changes.

Consider the code in Listing 3 (which does not use the Observer pattern), where a Spreadsheet and a Graph object display different representations of the same data. When the data changes, these objects must be notified so they can update their displays. What would be the impact of adding extra views to the App class? And how you could enable/disable the views being updated?

public  class Spreadsheet {...}
public  class Graph {...}
public class Data {...} 
public class App extends Applet
{
    private Data data = new Data();
    //views
    private Graph graph = new Graph();
    private Spreadsheet ssheet = new Spreadsheet();
    //other views
    private void UpdateViews()
    {
        //Method called if the user has changed something
        graph.update( data );
        sheet.update( data );
        //other interested parties
    }
}

Listing 3: Example without the Observer

Fortunately the Observer pattern comes to the rescue! The Observer pattern decouples the concrete observers from the subject. Listing 4 shows a simple example of how the Observer pattern improves the design in Listing 3. Observer and Observable are part of the java.util package.

public  class Spreadsheet implements java.util.Observer 
{   
    //impl
    public void update(Observable  o, Object  arg)
    {
        //update the spreadsheet with the changed data
    }
}
public  class Graph implements Observer {...}
pubic class Data Extends java.util.Observable  
{
    //inherits all the methods to add/remove and notify 
    //observers from Observerable
    public void ChangeData()
    {
        //change the data then...
        setChanged(); // inherited protected method
        // notifyObservers() will invoke update() on all registered observers
        notifyObservers(this);  //inherited method
    }
}
public class App extends Applet
{
    private Data data = new Data();
    //views
    private Graph graph = new Graph();
    private Spreadsheet ssheet = new Spreadsheet();
    //other views...
    public class App()
    {
        //register the observers with the observable
        data.addObserver( graph )
        data..addObserver( ssheet )
        //add other types
    }
    private void UpdateViews()
    {
        //Method called if the user has changed something
        //update the data object
        data. ChangeData();
    }
}

Listing 4: Example with an Observer

So how do the Spreadsheet and Graph objects in Listing 4 get updated? Basically, the Observer pattern implements a kind of callback from the Observable object to the Observer; take a closer look at the method Data.ChangeData() in Listing 4.

With a brief tour of Observers and Iterators under our belts, let's quickly recap and then go on to examine how we can use the two patterns together.

Summary of iterator and observer design patterns

Here are the basic definitions of the two terms:

  • Iterator: An abstract mechanism for accessing the elements in a collection.

  • Observer: The Observer pattern has two participants, Observers and Observables and defines a one to many relationship between them. An Observerable object contains a list of its Observers. When the Observable's state changes it notifies its Observers so that they are updated automatically.

For more information on design patterns, including Iterator and Observer, see "Design Patterns" Gamma et al, Addison Wesley. (See the Resources section for a link to this site.)

Iterator observer

Iterator client code takes the form of Listing 5, where the client is responsible for writing the loop, getting the contents, and processing them.

while( enumeration.hasMoreElements() )
{
    //1. get element
    //2. process
}

Listing 5: Generic iterator client code

In systems that use Iterators extensively, it is quite common to find many occurrences of the generic client code; frequently these iterators are abstractions over database query results or products of CORBA server objects in multitier architectures.

The goal of the Iterator Observer is to factor out the common client code and place it in the iterator itself. If you examine the code in Listing 5, you will find that the generic code is 1) the while loop, and 2) getting the element at the current position, whereas the client specific code is the processing of that element.

Listing 6 introduces the ObservableEnumeration class, which acts as an observable iterator and delegates to an Enumeration instance.

public class ObservableEnumeration extends java.util.Observable
{
    private Enumeration iter;
    public ObservableEnumeration( Enumeration e)
    {
        iter=e;
    }
    public void Iterate()
    {
        int num_observers=countObservers();
        if(num_observers>0)
        {
            while(iter.hasMoreElements())
            {
                setChanged();
                notifyObservers( iter.nextElement() );
            }
        }
        else
        {
            //throw an exception 
            //method called without registering
            //any observers
        }
    }
    public void Iterate(Observer [] observers )
    {
        int num=observers.length;
        while(iter.hasMoreElements())
        {
            for( int I=0;I<num;I++)
                observers[I].update( this, iter.nextElement() );    
        }
    }
    public void Iterate(Observer observer )
    {
        Observer [] obs = new Observer[1];
        obs[0] = observer;
        Iterate( obs );     
    }       
}

Listing 6: Example of observable Iterator

Class ObservableEnumeration has three overloaded methods called Iterate:

  1. Iterate(): Used if the client has registered Observers.

  2. Iterate(Observer observer): Allows clients to pass an Observer instance without the overhead of registering/deregistering.

  3. Iterate(Observer [] observers): Allows clients to pass an array of Observers without the overhead of registering/deregistering themselves.

ObserverableEnumeration offers a flexible extension to the Enumeration class. Listing 7 shows an example of client code using this class. See how simple it is to write a client of the ObserverableEnumeration? The actual iteration is removed from the client.

public class NewDisplay extends Panel implements java.util.Observer
{
    private Choice items = new Choice();
    //ctor and init code
    //Observer impl
    public void update( Observable observerable, Object arg )
    {
        items.addItem( (String)arg );
    }
}
public class Test2
{
    private Vector          v   = new Vector();
    private Hashtable       ht   = new Hashtable();
    private NewDisplay      disp     = new NewDisplay();
    private void Setup(Enumeration e)
    {
        ObservableEnumeration oe
            = new ObservableEnumeration( e );
        oe.Iterate( disp );
    }
    public void WithVector()
    {
        Setup( v.elements() );
    }
    public void WithHashTable()
    {
        Setup( ht.elements() );
    }
}

Listing 7: Using the Observable Iterator

Although the example in Listing 7 is quite simple, it demonstrates how the Observable Iterator can be used. Listing 8 looks at a more realistic example in which the class QueryExecuter is an abstraction hiding the underlying persistent storage -- in this case a relational database through JDBC.

public class QueryExecuter extends Observerable 
{
    private String      currentOp="none";
    private ResultSet   resultSet;
    private Connection  dbConnection;
    public QueryExecuter()
    {
        //impl to attach to source
        //assign dbConnection
    }
    public boolean GetEmployees()
    {
        currentOp="employee";
        Statement stmt = dbConnection.createStatement();
        String sql = "select * from EMPLOYEE";
        //execute query and cache the results
        resultSet = stmt.executeQuery(sql);
        return resultsSet.next(); 
    }
    //other Gets
    public void Iterate(Observer observer)
    {
        //iterate stored results
        //make the appropriate object
        if(currentOP.equals("employee") )
        {
            do
            {
                //make an Employee object
                Employee emp= new Employee();
                //assign attributes from resultsSet;
                observer.Update(this,emp); 
            } while( resultSet.next() );
        }
    }
    public Iterate(Observer [] observers )
    {
        //impl
    }
}
public class EmployeeDisplay extends Panel implements Observer
{
    private List empList = new List();
    //ctor etc
    //Observer impl
    public void update( Observable observerable, Object arg )
    {
        Employee emp=(Employee)arg;
        empList.addItem( emp.GetName() );
    }
}
public class Test3
{
    private QueryExecuter executer= new QueryExecuter();
    private EmployeeDisplay disp = new EmployeeDisplay();
    public void TestGetEmployees()
    {
        if( executer.GetEmployees())
        {
            executer.Iterate( disp );
        }
    }
}

Listing 8: The Observable Iterator in action with JDBC

Although it is not used in the example in Listing 8, QueryExecuter extends Observable. This gives clients the option of registering Observers rather than passing them as parameters. The method Test3.TestGetEmployees() first invokes the GetEmployees() method. If this method returns true, the query has succeeded and Test3.TestGetEmployees() proceeds by invoking the QueryExecuter.Iterate() and passing the EmployeeDisplay object as a parameter. (Remember: EmployeeDisplay implements Observer.) To make the example in Listing 8 easier to understand, I have used String currentOp to store the last query type. (For a better way of doing this, see my JavaWorld article on "Typesafe Constants.") This allows the Iterate() method to make the appropriate concrete object to pass to its Observers.

Related:
1 2 Page 1