CMPT 300 Design Patterns
May 2008
Lab 5 - The Adapter & Facade Patterns
Adapter Pattern
The Adapter pattern "coverts the interface of a class into another
interface that clients expect. The Adapter pattern allows classes work together
that otherwise could not because of incompatible interfaces."
In UML, it appears as
An Example
- Target.java
- Adapter.java
- Adaptee.java
- TestAdapter.java
A More Interesting Example
At first, a simple swing program:
- PlanetClientNoAdapter.java
This program uses a JFrame which expects JComponents.
Now, suppose you have the following Icon
- PlanetIcon.java
It draws planets.
The client is a JFrame which expects
JComponents, not Icon objects.
We can adapt the PlanetIcon using
the following adapter:
- IconAdapter.java
It is important to note that an IconAdapter IS-A JComponent!
The client using this adapter:
- PlanetClient.java
In UML
The Adapter Lab
Way back in the days of Java 1.0, there was an Enumeration
interface.
An example:
- EnumerateTest.java
Beginning with Java 1.2, using an Iterator
was recommended over Enumeration.
An iteration works as follows:
- IterateTest.java
However, old legacy code may still provide an Enumeration instead of an Iterator.
Consider the the API for OldStyleList:
Method
Signature
|
What
It Does
|
| public OldStyleList(); |
Creates an OldStyleList
|
| public boolean
add(Object e) |
Adds Object e to the list
|
| public Enumeration
elements() |
Returns an Enumeration of the elements
in the list
|
Now consider if client code wishes to use an Iterator - not an Enumeration - with OldStyleList. It cannot as OldStyleList only provides an Enumeration. The solution -> an Adapter!
In UML, we wish to accomplish the following:
Part I
Write the EnumerationAdapter class
so that a client can get an Iterator for an OldStyleList. That is, adapt the Enumerator so that it acts as an Iterator.
To test your adapter, modify IterateTest.java so that it works
with an OldStyleList (instead of
a Vector.)
Part II
OK, the OldStyleList class doesn't
really exist in the API; we made it up for this exercise. However, the ArrayList class
does exist and only provides an Iterator
(via the iterator() method it inherits
from the AbstractList class.) Write an IterationAdapter that does
just the opposite of what you completed in Part I. That is, it adapts an
iterator to an enumeration. Write some test code that allows you to insert
several items into an ArrayList and then perform an enumeration of these
items.
The Facade Pattern
The Facade pattern addresses the need where we need to simplify the interface
to an existing system.
Consider if we have the situation whereby a client must interface with
a database, a model, and some elements.
The client must first open the database to get a model. It must then query
the model to get an element. Lastly, it must use the element to get information
from the database. In UML, this appears as:
If we introduce the Facade, this now appears as:
Consider a system where we wanted to make some tea. This system may have
the following objects:
The code:
To make tea requires the following code:
TeaCup
blueCup = new TeaCup();
Water water = new Water();
TeaInfuser infuser = new TeaInfuser();
Tea tea = new Tea("Earl Grey");
infuser.addTea(tea);
water.boilWater();
blueCup.addWater(water);
blueCup.setSteepTime(15);
blueCup.steep();
More complete code:
This is where the facade pattern can come in useful
Part III
Write a class called TeaFacade.java
This class will have a constructor with the following signature:
public TeaFacade(TeaCup cup, Water water,
TeaInfuser infuser)
It will also provide the following method that makes tea:
public void makeTea(String teaType)
This method is passed a string with the name of the tea to make.
Now, rather than the client use the code shown above to make tea, it now
just has to do the following:
TeaFacade teaMaker = new TeaFacade(blueCup,water,infuser);
teaMaker.makeTea("Earl Grey");
Modify so that it now makes tea using the
TeaFacade.
>> It is important to note that the facade pattern is
not a demo program, rather, the facade that is created to simplify the interface
to a system becomes part of the API of the system. <<.
Executors
Notice how the system has to wait for the water to boil before is adds
tea to the infuser? We could have these activities performed in parallel
using an executor. An executor allows
multiple tasks to be run in parallel. Executors can be created with the following
code:
import java.util.concurrent.*;
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(/** a Runnable
task **/);
We must pass the service a task that implements the Runnable
interface.
This can be done with an anonymous inner class, such as the following
service.execute(new Runnable() { public void run() { infuser.addTea(tea); }
});
This allows the statement infuser.addTea(tea);
to be performed in parallel with other statements in the program.
Part IV
First modify MakeTeaInParallel.java
so that it uses the executor to run addTea() in an executor. Once
you get this working, modify your version
of MakeTea.java that
uses TeaFacade so that you invoke
addTea() using the
executor.
Threads?
For those of you familiar with threads, executors sound awfully familiar.
They are indeed, but with a much richer concurrency library.
|
Before we conclude with executors (for now), it is interesting to note
the Executors
practices a pattern we have recently seen. What pattern?