Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Java Tip 38: The trick to "Iterator Observers"

Factor out common code and make your Iterators observable

  • Print
  • Feedback

Page 3 of 4

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.

Those readers interested in hiding the nature of their systems' persistent storage should try to design a generic interface for the QueryExecuter that doesn't require explicit method names such as GetEmployees(). Another point to note here is that by encapsulating your queries in a class like QueryExecuter, a more scalable design results. Just consider the impact on your system's design if you later have to replace your RDBMS with a ODBMS and you have designed your system to pass SQL statements and JDBC ResultSet objects around!

Conclusion

As with all idioms and patterns, it is important to use them to solve a problem -- not to introduce complexity. Below I have listed the occasions when the Iterator Observer idiom can be extremely effective.

About the author

Philip Bishop is technical director and a consultant at Eveque Systems Ltd in the U.K, which specializes in designing and implementing distributed object systems using Java, CORBA, C++, and ODBMS.
  • Print
  • Feedback

Resources