The Adapter pattern pretty much does what you’d expect. It’s easy to remember. It’s easy to implement. But can it be… smooth?
The Adapter Pattern is pretty nifty. And it’s nifty on several levels.
Like most patterns, it’s approachable. But it goes a step beyond familiar. Even if you’ve never used it before, you can probably guess why and how to use it. The “why” is that you’re given a weird API that you need to make work with your existing code. More to the point, you need to make that weird code work like your existing code.
The “how” is wrapping it in a class that does just that. That adapts weirdness (or even just quirkiness) so that your application can use it like your existing code.
Even if you’ve never intentionally used the pattern, you’re no doubt already thinking how you’d approach the implementation. And initial instincts are probably right, so let’s take a look.
Consider… a smooth drone. It can do all the things. It can move up. It can move down. It can move left, right, forward and backward. What more could you want?
To control it, we build a remote control. It takes a drone and… moves it up, down, left, right, forward, and backward. And it’s easy and intuitive to use.
And then the unforeseen happens. The remote proves so easy and so intuitive that customers beg to give money to control other drones. Starting with… the crazy drone.
CrazyDrone doesn’t expose our nice six directional controls, however. It has two:
move(). So let’s roll up our sleeves and start adapting.
First, we import the crazy drone library so that it can be adapted. The
CrazyDrone class is the Adaptee–the class being adapted.
The Target for the adaptee is the
Drone interface. That is, we’re targeting the
moveDown(), move left, right, forward and backward methods.
We need to adapt the adaptee to that target. We need to adapt
CrazyDrone to the
Drone interface. We need… a
CrazyAdapter class. The
CrazyAdapter takes an instance of
CrazyDrone to move around. We’ll do it in the class definition, but it would be just as easy to do this in the constructor or as a setter method.
CrazyAdapter is adapting the
Drone interface, it is implementing the
Drone interface. So, it needs a
moveUp()method. To move the crazy drone up, we first rotate it up, then… move it. The five remaining movement methods are copies of this, save that they first rotate in different direction. Down. Left. Right. Forward. And… backward.
One last thing that our adapter needs to do is add a bit of functionality. The
CrazyDrone lacks a
location getter to report the current x-y-z location of the drone. To fully implement the
Drone interface, we have to add this feature. So the getter method returns the crazy drone’s
z properties, interpolated into a string.
With that we’re ready for the maiden flight of the
CrazyDrone using our very intuitive remote control. No changes are required of the remote control. Since the adapter class implements the
Drone interface, the remote control can use it just like the smooth drone.
So, after the smooth flight, we plug the crazy adapter drone into the remote, make the same flight, and… it works! The same flight results in the same result. Even though the crazy drone’s underlying controls are very different than the smooth drone, the remote is none-the-wiser.
The crazy drone may never be smooth, but as far as the remote is concerned, they are identical. We have successfully adapted the crazy drone to the same interface. And that’s.. a basic adapter pattern.
Now, the fun’s only getting started here. There are a couple of approaches that we can take with the pattern — each with their own consequences.
But beyond that, what makes the pattern so intriguing are implications and consequences that come about because of whywe use the pattern. Other patterns can take on a similar structure, but the consequences of the Adapter pattern are very specific to this pattern because it adapts code to our existing application.
In that respect, the Adapter pattern cuts to the heart of what patterns are and how to use them. Not to design, but to be aware of the consequences when we happen to be using them — especially those that arise from the intent of the pattern.
Oh, and naturally those consequences and implications are going to be some pretty darn thunky (and compendious) fun. So stay tuned!