In this chapter we’re going to attempt such impossible feats as putting a square peg in a round hole. Sound impossible? Not when we have Design Patterns. Remember the Decorator Pattern? We wrapped objects to give them new responsibilities. Now we’re going to wrap some objects with a different purpose: to make their interfaces look like something they’re not. Why would we do that? So we can adapt a design expecting one interface to a class that implements a different interface. That’s not all; while we’re at it, we’re going to look at another pattern that wraps objects to simplify their interface.
You’ll have no trouble understanding what an OO adapter is because the real world is full of them. How’s this for an example: Have you ever needed to use a US-made laptop in Great Britain? Then you’ve probably needed an AC power adapter...
You know what the adapter does: it sits in between the plug of your laptop and the British AC outlet; its job is to adapt the British outlet so that you can plug your laptop into it and receive power. Or look at it this way: the adapter changes the interface of the outlet into one that your laptop expects.
Some AC adapters are simple—they only change the shape of the outlet so that it matches your plug, and they pass the AC current straight through—but other adapters are more complex internally and may need to step the power up or down to match your devices’ needs.
Okay, that’s the real world; what about object-oriented adapters? Well, our OO adapters play the same role as their real-world counterparts: they take an interface and adapt it to one that a client is expecting.
Say you’ve got an existing software system that you need to work a new vendor class library into, but the new vendor designed their interfaces differently than the last vendor:
Okay, you don’t want to solve the problem by changing your existing code (and you can’t change the vendor’s code). So what do you do? Well, you can write a class that adapts the new vendor interface into the one you’re expecting.
The adapter acts as the middleman by receiving requests from the client and converting them into requests that make sense on the vendor classes.
It’s time to see an adapter in action. Remember our ducks from Chapter 1? Let’s review a slightly simplified version of the Duck interfaces and classes:
Here’s a subclass of Duck, the MallardDuck.
Now it’s time to meet the newest fowl on the block:
Now, let’s say you’re short on Duck objects and you’d like to use some Turkey objects in their place. Obviously we can’t use the turkeys outright because they have a different interface.
So, let’s write an Adapter:
① The client makes a request to the adapter by calling a method on it using the target interface.
② The adapter translates the request into one or more calls on the adaptee using the adaptee interface.
③ The client receives the results of the call and never knows there is an adapter doing the translation.
The Adapter Pattern converts the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.
Now, we know this pattern allows us to use a client with an incompatible interface by creating an Adapter that does the conversion. This acts to decouple the client from the implemented interface, and if we expect the interface to change over time, the adapter encapsulates that change so that the client doesn’t have to be modified each time it needs to operate against a different interface.
We’ve taken a look at the runtime behavior of the pattern; let’s take a look at its class diagram as well:
The Adapter Pattern is full of good OO design principles: check out the use of object composition to wrap the adaptee with an altered interface. This approach has the added advantage that we can use an adapter with any subclass of the adaptee.
Also check out how the pattern binds the client to an interface, not an implementation; we could use several adapters, each converting a different backend set of classes. Or, we could add new implementations after the fact, as long as they adhere to the Target interface.
Now despite having defined the pattern, we haven’t told you the whole story yet. There are actually two kinds of adapters: object adapters and class adapters. This chapter has covered object adapters and the class diagram on the previous page is a diagram of an object adapter.
So what’s a class adapter and why haven’t we told you about it? Because you need multiple inheritance to implement it, which isn’t possible in Java. But, that doesn’t mean you might not encounter a need for class adapters down the road when using your favorite multiple inheritance language! Let’s look at the class diagram for multiple inheritance.
Look familiar? That’s right—the only difference is that with class adapter we subclass the Target and the Adaptee, while with object adapter we use composition to pass requests to an Adaptee.
If you’ve been around Java for a while you probably remember that the early collection types (Vector, Stack, Hashtable, and a few others) implement a method, elements(), which returns an Enumeration. The Enumeration interface allows you to step through the elements of a collection without knowing the specifics of how they are managed in the collection.
The newer Collection classes use an Iterator interface that, like Enumeration, allows you to iterate through a set of items in a collection, but also adds the ability to remove items.
We are often faced with legacy code that exposes the Enumeration interface, yet we’d like for our new code to use only Iterators. It looks like we need to build an adapter.
First we’ll look at the two interfaces to figure out how the methods map from one to the other. In other words, we’ll figure out what to call on the adaptee when the client invokes a method on the target.
Here’s what the classes should look like: we need an adapter that implements the Target interface and that is composed with an adaptee. The hasNext() and next() methods are going to be straightforward to map from target to adaptee: we just pass them right through. But what do you do about remove()? Think about it for a moment (and we’ll deal with it on the next page). For now, here’s the class diagram:
Well, we know Enumeration just doesn’t support remove. It’s a “read only” interface. There’s no way to implement a fully functioning remove() method on the adapter. The best we can do is throw a runtime exception. Luckily, the designers of the Iterator interface foresaw this need and defined the remove() method so that it supports an UnsupportedOperationException.
This is a case where the adapter isn’t perfect; clients will have to watch out for potential exceptions, but as long as the client is careful and the adapter is well documented this is a perfectly reasonable solution.
Here’s simple but effective code for all those legacy classes still producing Enumerations:
You’ve seen how the Adapter Pattern converts the interface of a class into one that a client is expecting. You also know we achieve this in Java by wrapping the object that has an incompatible interface with an object that implements the correct one.
We’re going to look at a pattern now that alters an interface, but for a different reason: to simplify the interface. It’s aptly named the Facade Pattern because this pattern hides all the complexity of one or more classes behind a clean, well-lit facade.
You’ve done your research and you’ve assembled a killer system complete with a DVD player, a projection video system, an automated screen, surround sound, and even a popcorn popper.
Check out all the components you’ve put together:
You’ve spent weeks running wire, mounting the projector, making all the connections, and fine tuning. Now it’s time to put it all in motion and enjoy a movie...
Pick out a DVD, relax, and get ready for movie magic. Oh, there’s just one thing—to watch the movie, you need to perform a few tasks:
① Turn on the popcorn popper
② Start the popper popping
③ Dim the lights
④ Put the screen down
⑤ Turn the projector on
⑥ Set the projector input to DVD
⑦ Put the projector on wide-screen mode
⑧ Turn the sound amplifier on
⑨ Set the amplifier to DVD input
⑩ Set the amplifier to surround sound
⑪ Set the amplifier volume to medium (5)
⑫ Turn the DVD player on
⑬ Start the DVD player playing
Let’s check out those same tasks in terms of the classes and the method calls needed to perform them:
But there’s more...
When the movie is over, how do you turn everything off? Wouldn’t you have to do all of this over again, in reverse?
Wouldn’t it be as complex to listen to a CD or the radio?
If you decide to upgrade your system, you’re probably going to have to learn a slightly different procedure.
So what to do? The complexity of using your home theater is becoming apparent!
Let’s see how the Facade Pattern can get us out of this mess so we can enjoy the movie...
A Facade is just what you need: with the Facade Pattern you can take a complex subsystem and make it easier to use by implementing a Facade class that provides one, more reasonable interface. Don’t worry; if you need the power of the complex subsystem, it’s still there for you to use, but if all you need is a straightforward interface, the Facade is there for you.
Let’s take a look at how the Facade operates:
A facade not only simplifies an interface, it decouples a client from a subsystem of components.
Facades and adapters may wrap multiple classes, but a facade’s intent is to simplify, while an adapter’s is to convert the interface to something different.
To use the Facade Pattern, we create a class that simplifies and unifies a set of more complex classes that belong to some subsystem. Unlike a lot of patterns, Facade is fairly straightforward; there are no mind-bending abstractions to get your head around. But that doesn’t make it any less powerful: the Facade Pattern allows us to avoid tight coupling between clients and subsystems, and, as you will see shortly, also helps us adhere to a new object-oriented principle.
Before we introduce that new principle, let’s take a look at the official definition of the pattern:
The Facade Pattern provides a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.
There isn’t a lot here that you don’t already know, but one of the most important things to remember about a pattern is its intent. This definition tells us loud and clear that the purpose of the facade is to make a subsystem easier to use through a simplified interface. You can see this in the pattern’s class diagram:
That’s it; you’ve got another pattern under your belt! Now, it’s time for that new OO principle. Watch out, this one can challenge some assumptions!
But what does this mean in real terms? It means when you are designing a system, for any object, be careful of the number of classes it interacts with and also how it comes to interact with those classes.
This principle prevents us from creating designs that have a large number of classes coupled together so that changes in one part of the system cascade to other parts. When you build a lot of dependencies between many classes, you are building a fragile system that will be costly to maintain and complex for others to understand.
Okay, but how do you keep from doing this? The principle provides some guidelines: take any object; now from any method in that object, the principle tells us that we should only invoke methods that belong to:
The object itself
Objects passed in as a parameter to the method
Any object the method creates or instantiates
Any components of the object
This sounds kind of stringent doesn’t it? What’s the harm in calling the method of an object we get back from another call? Well, if we were to do that, then we’d be making a request of another object’s subpart (and increasing the number of objects we directly know). In such cases, the principle forces us to ask the object to make the request for us; that way we don’t have to know about its component objects (and we keep our circle of friends small). For example: