Session 30

Wrapping a Semester of Objects


810:062
Computer Science II
Object-Oriented Programming


Where Are We?

Last session, we learned about the strategy pattern. A strategy is an object that executes the piece of an algorithm that changes. This allows us to customize an an algorithm by passing the part that changes as a parameter.

We used a strategy object to implement a Play class that counts the words in its text that satisfy an arbitrary test. The test is passed as an argument to the word-counting method.

To do this, we created an interface for our strategy objects:

    public interface CountedFeature
    {
        public boolean hasFeature( String s );
    }

Then we wrote a strategy that finds words that start with a particular character:

    public class StartsWith implements CountedFeature
    {
        private char targetChar;

        public StartsWith( char target )
        { targetChar = target; }

        public boolean hasFeature( String s )
        {
            if ( s == null || s.length() == 0 )
                return false;
            return s.charAt(0) == targetChar;
        }
    }

Here's another strategy, one that finds words of a particular length:

    public class OfLength implements TestFeatureStrategy
    {
        private int targetSize;

        public OfLength( int size )
        { targetSize = size; }

        public boolean hasFeature( String s )
        {
            if ( s == null )
                return false;
            return s.length() == targetSize;
        }
    }

The folks in the English department are pleased with us! They study texts to determine who the author was, based on stylistic criteria such as average word length, number of different words, etc. Our program allows them to do this with great ease.

But...


Your Final Bow

Now they want more. (Clients... Sheesh.)

They would like to be able
to combine tests, such as
"count all the 4-letter words
that start with 's'".

You think to yourself, "Hey, I know just what to do! Doing two tests at once is just another kind of test. All I need is another class that implements CountedFeature.

"I sure am happy I took Computer Science II!! I always knew that all my effort would be worth something some day."

You dig out your notes and
implement a "compound test" class
for our application.

Do it now, please. (Humor me one last time...)


Evolving Solutions

Here is the least flexible solution I can imagine. How could it be better?

First of all, it repeats code. Here is a solution with less duplication that solves the problem. Well, sort of. What was the problem?

Our clients want to be able to combine two tests -- any tests, not just words with a particular first character and length.

What we need is an object that acts like a single test but that does two tests. Each of these tests is just a single test like the ones we've seen before.

What does this sound like? Like a Panel or a SequenceInputStream. We can use the composite pattern!

Here is a solution that accepts any two CountedFeatures. Look how little code this class requires...

If we do this, we might want to generalize our solution one more step and let a compound test perform any number of tests. Rather than hold two "worker tests", it will hold a collection of "workers".

[ ... run this demo program that takes initial and word length as command-line args ... ]

Now we have a solution that is no more complicated than our first solution and much more flexible. When the client asks for a three-part test, we can deliver the solution for free!


The Composite Pattern

Our CompoundFeature class is an example of the composite pattern. As we learned earlier, a composite is an object that acts like one object but is, in fact, coordinating the work of several objects.

the composite pattern

We've now seen composites several times, in Java, including...

... and out:


An Alternative Implementation for CompoundFeature

How else might we build a CompoundFeature?

We could let the client pass a whole collection of tests at one time. Rather than require the client to send a particular kind of collection, we can take advantage of the iterator pattern:

Can you think of any advantages or disadvantages to this new approach?

Having alternative ideas for implementation is A Good Thing! Making choices among them can be hard, but as you gain experience you'll feel more comfortable making more kinds of design decisions... and the result will be better designs!


So, What's the Answer?

Let's run our new compound test:

    mac os x > java CompoundFeatureDemo hamlet.txt 5 t
    The play in file hamlet.txt contains
                     463 5-letter words that start with t.

    It contains 3785 5-letter words,
    and it contains 4533 words that start with t.


    mac os x > java CompoundFeatureDemo bible.txt 5 t
    The play in file bible.txt contains
                     12601 5-letter words that start with t.

    It contains 95526 5-letter words,
    and it contains 154575 words that start with t.


    mac os x > java CompoundFeatureDemo declaration.txt 5 t
    The play in file declaration.txt contains
                     114 5-letter words that start with t.

    It contains 828 5-letter words,
    and it contains 1110 words that start with t.


My Final Bow

Back in Session 28, we learned about the iterator pattern and how it simplified code that has to handle different kinds of collections. We left that day with one dissatisfaction: Because Java arrays are not objects, they don't know how to create an Enumeration of themselves. How could we use an array then for our BankAccount class?

We can write our own Enumeration class!

It really isn't all that hard. How do process an array with a loop in our own code? We initialize a counter to 0, then use it to access consecutive elements in the array, incrementing the counter on each pass. We stop when the counter reaches the size of the array.

An Enumeration responds to two messages, hasMoreElements() and nextElement(). An ArrayEnumeration could keep track of a counter, initialized to 0. Each time it is asked if it hasMoreElements(), it can make sure the counter is less than the array size. Each time it is asked for the nextElement(), it returns the item at the counter's slot in the array.

Here is a simple ArrayEnumeration class that fits the bill. What do you think?

(If we want our object to act like other Enumerations, then we need to do one more thing. If a client tries to access more elements than are available, the ArrayEnumeration should throw a NoSuchElementException -- not the ArrayIndexOutOfBoundsException that arrays throw. To do this, we only need to add a single if statement to the code. Check it out.)

If we ever want to have a new kind of object, we write a new class. That's the essence of object-oriented programming.


Some Final Ideas on Object-Oriented Design

From the article Principles of OO Design (Part 1) or, Everything I know about programming, I learned from Dilbert (by Alan Knight, Smalltalk Chronicles Vol. 2, No. 1, March 2000), I have identified a few of the ideas you would like to grok to know object-oriented design -- see these slides.


Are There Any Questions?

is it over yet???

Any Questions?

"What's on the exam?"


Wrap Up


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