Session 27

The Adapter as a Design Pattern


810:062
Computer Science II
Object-Oriented Programming


The Idea of a Design Pattern

A design pattern is simply a standard solution for solving a common problem.

This solution is standard because all programmers are trying to balance a common set of forces that occur when we try to solve that class of problems.

A couple of weeks ago, we had couple of sessions on a technique that we called a decorator. Decorator is a design pattern. It solves the problem of adding a behavior to every class in a hierarchy. This is a remarkably common problem. Among the forces that a decorator balances are:

The solution is to use a common interface for all classes, and to write a class that delegates the real work to an instance that implements the same interface.

We saw the same problem arise when studying Java's stream classes, and the solution worked much the same way: FilterInputStream.

We also encountered the composite pattern twice: first in the form of graphical Panels, and second in the form of the SequenceInputStream.


An Exercise

Suppose that we are building a simulation program in Java to help students learn how to use an operating system, such as Linux. Our simulation will show graphically how various commands work. For example, we might demonstrate:

mv index.html 062-dir/

... by showing a rectangle labeled index.html moving from one box labeled with the name of the current directory into a box inside that box, labeled 062-dir.

picture of moving a file

Suppose that we have built a hierarchy of icons like boxes and rectangles:

    public interface SimulationIcon
    {
        public void draw( Graphics target );
        public void move();
        public int  y();
    }

    public class RectangleIcon implements SimulationIcon
    {
        public void draw( Graphics target )
        { ... }

        public void move()
        { ... }

        public int y()
        { ... }
    }

We have decided to add to our program the ability to demonstrate Unix commands that manipulate processes. For instance, we would like to show users what happens when they print a file:

lp -d lj3sia index.html

To do so, we need a new icon o represent processes, and we decide to use a shaded circle.

... then we remember that we have already implemented a hierarchy of Ball classes that have color, paint themselves, move, and so on.

Unfortunately, the interface of the Ball class does not match the interface expected by our simulation, the SimulationIcon interface.

Implement a CircleIcon class for our simulator,
without duplicating Ball's code.


Possible Solutions

We could just change something to "make it fit":

When might these be reasonable solutions? When not?

  1. We could change the Ball classes to match the SimulationIcon interface.

  2. We could change the SimulationIcon interface to match the Ball classes.

On second thought, this is the same problem I face when I try to plug 1990s-era appliances in at my 1950s house: the prongs on the receptacle don't match the prongs on my power cord. (It happened again yesterday!!) How do I solve that problem? I use an adapter.

a simple adapter

Maybe we can use the same idea...


One Solution: A Class Adapter

A CircleIcon could be a Ball:

a class adapter
    public class CircleIcon extends Ball implements SimulationIcon
    {   ...
        public void draw( Graphics target )
        {
            paint( target );
        }

        // public void move() -- INHERITED!

        public int y()
        {
            return super.y();
        }
        ...
    }

Notice that there is nothing new here... just a useful way to combine existing ideas to solve a problem.

You know a lot of interesting ideas now. Getting better at design is mostly a matter of learning how to use those ideas most effectively.


Another Solution: An Object Adapter

A CircleIcon could use a Ball:

a object adapter
    public class CircleIcon implements SimulationIcon
    {
        private Ball icon;
        ...
        public void draw( Graphics target )
        {
            icon.paint( target );
        }

        public void move()
        {
            icon.move();
        }

        public int y()
        {
            return icon.y();
        }
    }

What does this look like? How is it different?


The Adapter Pattern

The basic idea of an adapter is this:

generic idea of adapter

The adapter pattern is an idea that you can use in a lot of different ways:


Handling Mouse Events

MouseAdapter? That class showed up in your reading assignment for today...

Our BallWorlds and CannonWorld games only handle events that generated by the active components -- a button and a slider -- that we added to it.

More generally, though, we will want to trap and respond to a mouse action anywhere within the frame.

Any listener that wishes to monitor mouse activity must implement the MouseListener interface:

     public interface MouseListener
     {
        public void mouseClicked ( MouseEvent e );
        public void mouseEntered ( MouseEvent e );
        public void mouseExited  ( MouseEvent e );
        public void mousePressed ( MouseEvent e );
        public void mouseReleased( MouseEvent e );
     }

As you should guess, the MouseListener interface is part of the java.awt.event package. It specifies what an object must do to be a mouse listener within the Java Event Model.

Consider this simple frame that displays the coordinates of every mouse press. It defines a MouseKeeper class that listens for mouse events. It responds to only one: mousePressed(). The mouse listener maintains state -- the coordinates of the last mouse press -- that the frame accesses each time it paints itself.

Notice that we have to define empty methods for the other four responsibilities of mouse listeners, because the interface requires them.


A Little Exercise

Modify MouseListenerFrame so that it:

Here's a possible solution, MouseClickDistanceFrame. Now we have to define an active mouseReleased() method, to record the coordinates of the release and tell the frame to repaint itself.

This time, I stored the points as IVs in the frame. Remember: the inner class has access to all the private stuff of its containing class, and the containing class has access to everything in the inner class.

(Note: Java 1.5 introduces "static imports", so that we don't have to use the full name of Math.pow() and Math.sqrt() in our code.)


An Alternative to Empty Methods

We often find ourselves writing classes like MouseListenerFrame and MouseClickDistanceFrame, in which we want to define only one or two of the required methods in the MouseListener interface. Do we really have to write them all?

Not really. We just have to be sure that our class has them all. We can do that using an AWT class named MouseAdapter. Here's how:

This is yet another form of the Adapter pattern, one that fits an object with a smaller vocabulary into a slot that requires an object with a larger vocabulary.

Here's an alternative MouseClickDistanceFrame that defines its listener as a subclass of MouseAdapter. As a result, because we only need behavior on mouse presses and mouse releases, we only have to write those two methods.


When Should We Use an Adapter?

The key to knowing how to design programs is knowing when to use which technique -- and why. In order to know answers to these questions, you need to know the trade-offs that each solution makes.

These are the things I called forces earlier: the issues we face when building an application that lead us to make certain design decisions. A design pattern needs to give us some idea how to make these decisions and thus whether (and how) to use the pattern.

For example, using an adapter for an m-to-n "plug":

More specifically, the forces driving a class adapter include:

      ... which are great, as long as you need to adapt only one class.

And the forces driving an object adapter include:

      ... which is great, as long as you can pay the run-time price of delegating to the server every message sent by the client to the adapter.


A Quick Exercise

The decorator pattern and the adapter pattern look an awful lot alike.

Why aren't they the same pattern?

In other words: what is the difference between the two?

The key is in their intent:

Adapters wrap an object to change its interface, without (necessarily) changing the object's behavior.

Decorators wrap an object to extend its behavior, without (necessarily) changing the object's interface.

Many of the design patterns you see in class and elsewhere use inheritance and composition in similar ways. The solutions will often be different in subtle ways.


The Idea of Design Patterns

When writing a program, you often face a problem and think to yourself, "What do I do now?"

In object-oriented programming, that question usually turns into, "What kinds of objects should I create, and how should they communicate?"

Then you stumble about looking for a solution. Sometimes, you will create a good solution. More often, your first few solutions will work just well enough. Over time, you refactor the code in the face of changing specs. As you go, you will create better and better solutions.

After a while, you begin to recognize the same problem showing up in different guises, so you begin to know up front what sort of solution you need.

Programming becomes easier as your experience grows, because you recognize common problems and know stock solutions for solving them.

For the next few sessions, we will study a small catalog of object-oriented design patterns: common problems and stock solutions for solving them.

Studying these patterns is a way for you to increase your design vocabulary more quickly than learning patterns by trial and error. They also give you more and more examples of inheritance and polymorphism.

So far in class, we have encountered a number of standard design patterns, including:

Pattern Examples
Decorator decelerating balls, input streams
Adapter StringBufferInputStream, SimulationIcons, MouseAdapter
Composite frames and panels in the AWT, SequenceInputeStream
Strategy layout managers in the AWT

Most of these patterns, and many basic OOP designs, are examples of a more elementary pattern called Polymorphic Object, which is a substitutable object.


Wrap Up


Eugene Wallingford ..... wallingf@cs.uni.edu ..... November 30, 2004