Session 28

The Iterator Pattern


810:062
Computer Science II
Object-Oriented Programming


Opening Exercise

The Background

Suppose that we have implemented a simple BankAccount class as part of a suite of financial classes. Among other things, BankAccounts keep track of the transactions that have been applied to it:

    public class BankAccount
    {
        private Vector allTransactions;
        ...

        protected Vector transactions()
        {
            return allTransactions;
        }

        ...
    }

We created the transactions() method so that subclasses of BankAccount can access the inherited vector of transactions. Such subclasses might define particular kinds of accounts (say, CheckingAccount or IndividualRetirementAccount), or add special behavior for use in specific situations.

A Transaction is an object that satisfies this interface:

    public interface Transaction
    {
        ...
        public int  execute( int balance );
        public void print  ();
        public int  value  ();
       ...
    }

The Set-Up

In order to comply with FBI regulations, we need to implement a subclass of BankAccount called FilterBankAccount, which provides client code with the ability to review all of the account's Transactions with a value larger than some value specified by the client. For example:

    FilterBankAccount mobsterLaundry = getBadGuysAccount();

    Vector suspiciousTransactions =
              mobsterLaundry.largeTransactions( 1000000 );

suspiciousTransactions must now contains references to all transactions in mobsterLaundry's collection that have values greater than 1000000.

The Work

So we need to implement FilterBankAccount with at least this code:

    public class FilterBankAccount extends BankAccount
    {
        ...

        public Vector largeTransactions( int threshold )
        {
            // DA BLANK
        }
    }

Your job: fill in DA BLANK.


Possible Solution (v1)

This is a standard filtering problem from CS1. Here is a typical solution:

    public Vector largeTransactions( int threshold )
    {
        Vector myTransactions = transactions();
        Vector subset         = new Vector();

        for (int i = 0; i < myTransactions.size(); i++)
        {
            Transaction t =
               (Transaction) myTransactions.elementAt(i);

            if ( t.value() > threshold )
                subset.addElement( t );
        }

        return subset;
    }

This is a standard filtering method, which requires us to process all of the items in a collection with a guarded action. Guarded action is a design pattern, too, though at a lower level.

(An alternative would be to implement a BankAccount decorator class that shows only Transactions larger than a specified threshold... Give it a try!)


Same Problem, Different Data Structure (v2)

Suppose that we have decided to modify the BankAccount class to store its Transactions in an array instead of in a Vector. So we decide to have the transactions() method return an array, too.

Modify your FilterBankAccount code.

Here is a typical solution:

    public Vector largeTransactions( int threshold )
    {
        Transaction[] myTransactions = transactions();
        Vector        subset         = new Vector();

        for (int i = 0; i < numberOfTransactions(); i++)
        {
            Transaction t = myTransactions[i];

            if ( t.value() > threshold )
                subset.addElement( t );
        }

        return subset;
    }


Same Problem, Yet Another Data Structure (v3)

Or what if a BankAccount stores its Transactions in an ArrayList instead of a Vector? How would that change your solution?

    public Vector largeTransactions( int threshold )
    {
        ArrayList myTransactions = transactions();
        Vector    subset         = new Vector();

        for (int i = 0; i < myTransactions.size(); i++)
        {
            Transaction t = myTransactions.get( i );

            if ( t.value() > threshold )
                subset.addElement( t );
        }

        return subset;
    }

What if we decided to write our own collection class called TransactionList, which implements the interface java.util.List? How would that change your solution?

Or suppose I used a HashMap, whose interface you don't know yet. What then??


Interface Overload

Aaagggggghhhhhhhh!

The problem: We know how to write this filtering code, probably in our sleep by now. But in order to do so, we must remember a different "accessor" interface for each data structure that we use. This solution also couples our client code to the data structure it uses:

This can't be good.


Avoiding Interface Overload

Programmers are especially annoyed when a change in data structure causes a gratuitous change in client code -- for example, on the change from a Vector to an array.

There has to be something better here. Object-oriented programming is all about...

Why should the user of an class, even a subclass, have to be concerned with the implementation details of the class?


The Solution: Make a New Kind of Object



Provide a common interface
for iterating over the items
in a collection in a sequential fashion,
without needing to know the details
of how the collection is represented.


This is the iterator pattern, used in every common object-oriented language.

One of Java's versions of the iterator pattern is the Enumeration, which is defined in the java.util package:

    public interface Enumeration
    {
        public boolean hasMoreElements();
        public Object  nextElement    ();
    }

An Enumeration provides sequential access to the items in the collection.


Iterators in the Real World

As we have noted before, most design patterns occur all over the real world, even in non-programming contexts. This shouldn't surprise us, given that people solve difficult problems in common ways and that we try write our programs in a way that is continuous with the world.

The iterator pattern is no different.

How about the TV remote control? I use remote controls in many different places:

The remote control is an iterator. Its up and down buttons allow the user to visit each available TV channel in turn!


Re-Solving the Earlier Exercise

Using an Enumeration, we can write code of the sort:

            while ( someEnumerationObject.hasMoreElements() )
                process someEnumerationObject.nextElement()

Your job: fill in DA BLANK again.
This time, use an Enumeration for your loop.

    public Vector largeTransactions( int threshold )
    {
        // DA BLANK
    }


Whoa, there, professor... Where do I get the Enumeration to process?


You ask the Vector to give you an Enumeration of its elements. The interface for the Vector class includes:

public final synchronized Enumeration elements()

Just send a Vector an elements() message, and it returns to you an Enumeration of the items it contains!

(Why do you suppose that this method is declared final? What does synchronized mean?)


The New Solution

How's this:

    public Vector largeTransactions( int threshold )
    {
        Enumeration myTransactions = transactions().elements();
        Vector      subset         = new Vector();

        while ( myTransactions.hasMoreElements() )
        {
            Transaction t =
               (Transaction) myTransactions.nextElement();

            if ( t.value() > threshold )
                subset.addElement( t );
        }

        return subset;
    }

Notice that how you do what you do with the Transactions in the vector myTransactions does not change -- only how you access them.

But now..

how you access them
is independent of the data structure!

That is beautiful.


The New Solution, A Different Data Structure

What if the Transactions had been stored in an ArrayList or a HashMap, instead of a Vector?

    public Vector largeTransactions( int threshold )
    {
        Enumeration myTransactions = transactions().elements();
        Vector      subset         = new Vector();

        while ( myTransactions.hasMoreElements() )
        {
            Transaction t =
               (Transaction) myTransactions.nextElement();

            if ( t.value() > threshold )
                subset.addElement( t );
        }

        return subset;
    }

*****       NO CHANGES !!       *****

This is the sort of data-independence that we would like for all of our programs to have.


Final Notes on Iterators

Two things:

Most design patterns offer infinite possibilities in how we use them, when we use them, and how we implement them. Iterator is no exception!


Going Pro: making a Double-Clickable .jar File

Some of you have written Name Surfer programs of which you are quite proud. The program makes for an enjoyable conversation piece. Now, you are working on Mastermind games. When you get done, you may want to share your handiwork with your friends, family, neighbors, and potential employers.

But many of those folks won't want (or know how!) to start your program from a Linux command line. You'd like to give them your program as an application like any other...

How you can you make a double-clickable application from your Java program?

Many industry-grade development environments include tools for creating double-clickable apps. For example, the developer tools in Mac OS X enable a programmer to create apps, design custom icons, and write all sorts of scripts for interacting with the OS. Unfortunately, the IDEs you are mostly likely to know at this point, such as jGrasp, do not. Nor do vi or emacs.

Many of these professional tools are quite complex. Learning them just for the sake of creating a double-clickable app seems too expensive for the pay-off.

But, if you do not make a double-clickable app, your program will be hard or impossible for many others to use. Your mom and dad, or grandma and grandpa, or five-year-old cousin, probably don't know how to fire up the Java compiler and Java compiler. They probably don't know how to fire up an IDE, either. And they don't want to learn -- nor should they need to.

Therefore, use the Java Development Kit's jar tool to create a double-clickable Java archive file.

The jar tool packages a bunch of files into a single archive file that you can give to another or offer for download on the web. If we tell the archive which class file contains the main() method, then users will be able to double-click on the file's icon and run it like a standalone application. The file that tells the archive this information is called a manifest file. The manifest acts like a "read me" file for the Java virtual machine can read. When we run jar, it automatically creates a manifest file for the archive. Unfortunately, the default manifest file does not include a definition of the class that contains the main() method.

Here are the steps you can use to create a double-clickable jar file. I'll illustrate them by creating a double-clickable Mastermind application, which you can use as a working demonstration as you do Homework 10.

  1. Create a manifest file for your application. You can download this template file and edit it. Change the string NAME-OF-CLASS-FILE-CONTAINING-MAIN to be the name of the class file that contains your main() method.

    For example, I changed the file to read:

                 Manifest-Version: 1.0
                 Created-By: 1.4.1
                 Main-Class: MasterMindApp
    

    because in my version of the program the file MasterMindApp.class contains the program's main() method.

  2. Place this file in the directory that contains your *.class files.

    I placed manifest.mf in my directory named mastermind/exe/.

  3. Go to your Unix shell (or perhaps Windows DOS window). Change into the directory containing your *.class files and manifest.mf.

    I changed into my directory named mastermind/exe/:

                 cd mastermind/exe/
    

  4. Use jar to create your archive. The command you need is:
                 jar cfm jarFileName manifest.mf *.class
    

    where jarFileName is what you want to call your archive.

    To create my archive, I entered:

                 jar cfm mastermind.jar manifest.mf *.class
    

  5. Move your jar file to whatever location you desire.

    I moved mine to the 062/web/homework/support/ directory, from which you could download it.

Voilá! And there you have it. Double-click on mastermind.jar and watch it go.

Making a Custom Icon

It's also fun to make your own icon for your double-clickable app. The standard "jar file" icon is so boring! Industrial-strength IDEs often provide tools that help us do this. jar does not.

I don't know how to do this in Linux or Windows, or if it is even possible. But in Mac OS X I simply open the double-clickable file's Information window and paste the image I want in place of the icon. So I did this:

  1. Played Mastermind and then took a snapshot of the board:

    snapshot of a Mastermind game

  2. Clipped out the colored peg portion of the interface:

    a Mastermind game board

  3. Pasted that image into the .jar file's Information window. Now I see this on my desktop:

    a Mastermind game board

My color scheme may not be the best, but that is a very nice touch.

You can read more about jar by typing "man jar" at your Unix prompt, or by visiting the JDK documentation.


Wrap Up


Eugene Wallingford ..... wallingf@cs.uni.edu ..... April 21, 2005