DaedTech

Stories about Software

By

Adapter

Quick Information/Overview

Pattern Type Structural
Applicable Language/Framework Agnostic OOP
Pattern Source Gang of Four
Difficulty Relatively simple

Up Front Definitions

There are no special definitions that I’ll use here not defined inline.

The Problem

In keeping with the vehicle theme from the previous post, let’s say that we have an application that models vehicle operation. Among other things, we have an abstract base vehicle class and some concrete vehicles, “car” and “boat.” The base class defines an abstract “Start()” method and the inheritors override this to define their own starting logic. For the sake of discussion, let’s say the purpose of this application is to graphically model wear and tear on vehicles as they make their way around. That is, we need to be able to move the vehicles around and simulate their internal components in terms of how they do over the course of time. A “Start()” method is vital to this.

Here is a very simple view of what this might look like:

public abstract class Vehicle
{
    public abstract void Start();
}
public class Car : Vehicle
{
    public override void Start()
    {

        //Car start logic elided (probably involves firing the starter object)
    }
}
public class Boat : Vehicle
{
    public override void Start()
    {
        //Boat start logic elided (maybe we're a motor boat and this involves pulling a rip-cord)
    }
}

As you can see, the details of everything but this inheritance relationship are omitted. Now, let’s say that this code was written a while ago, and all of the internal vehicle logic resides in an assembly that compiles to Vehciles.dll. Your company builds this and deploys it, and hundreds of users in the field are using it.

The client code for this is in another assembly or assemblies, and it contains various code and classes that do things like this:

public class VehicleSimulator
{
    private List _fleet;

    public VehicleSimulator(List fleet)
    {
        _fleet = fleet;
    }

    public void DeployFleet()
    {
        _fleet.ForEach(fl => fl.Start()); //Start the vehicles
        //Logic for sending them on their merry way elided
    }

}

In other words, the client code is using the vehicle code polymorphically. Let’s also impose some political constraints here. Let’s say that the client code that you deploy is written by Group A in your department and the car code written by Group B. Group B is in high demand and can’t be bothered to change the Vehicles.DLL unless it’s absolutely necessary and unavoidable. Let’s say that we’re part of group A, and a new requirement comes in.

The bigwigs at the company have just purchased a smaller company that models tanks for the military in the same sort of way that we do with other vehicles. We need to incorporate their Tank implementation into our vehicle modeling software. The now-defunct company has Tank.DLL, and Tank.DLL contains the following code:

public class Tank
{
    public void FireUpTheTank()
    {
        //Clearly, you do something more hardcore than "start" a tank
    }
}

Let’s also say that the bigwigs laid off everybody in the now acquired company, so there’s no real reliable access to their source code and build in the short term. In other words, because of the logistics of the acquisition, we’re stuck with the Tank.cs class as-is, and because of internal politics, we’re stuck with the Vehicle class as-is. We have to use those classes, and the only thing we can change is our client code. (This may be a little contrived, but it’s surprising how often things work out exactly this way in real world coding environments–the arbitrary and silly is often a fact of life.)

So we need to deploy our fleet, which now includes a tank. Let’s see how that looks:

public class VehicleSimulator
{
    private List _fleet;
    private List _tanks;

    public VehicleSimulator(List fleet, List tanks)
    {
        _fleet = fleet;
        _tanks = tanks;
    }

    public void DeployFleet()
    {
        _fleet.ForEach(fl => fl.Start()); //Start the vehicles
        _tanks.ForEach(tank => tank.FireUpTheTank());
        //Logic for sending them on their merry way elided
    }

}

Well, there you have it. We’ve doubled our code and gotten the job done. Now, every time we add new functionality, we’ll just have to add it in parallel for vehicle and tank. So, let’s build and ship this thing, and… wait a second! That’s absolutely ridiculous. We’re rampantly duplicating logic and creating a maintenance nightmare. We’re completely defeating the purpose of the polymorphism of vehicle. There has to be a better way to do this.

So, What to Do?

This is where the Adapter pattern comes in. What we really want here is a way to trick the existing API into thinking that Tank is just another vehicle. That shouldn’t be too hard, since Tank is just another vehicle.

So let’s try something. We’ll just create our own Tank called “TankAdapter” and include it in our client code assembly. We have access to the public members of Vehicle.DLL, so we’ll just define our own inheritor:

public class TankAdapter : Vehicle
{
    public override void Start()
    {
        //All well and good, but we need to start an actual tank at some point
    }
}

Now we’ve partially solved the problem. We can create a vehicle simulator as follows:

var myFleet = new List() { new Car(), new Boat(), new TankAdapter() };
var mySimulator = new VehicleSimulator(myFleet);

This keeps our polymorphism intact and restores the Simulator class’s internals to sanity. With TankAdapter, we have no need to create and maintain the special case for Tank.cs. However, it doesn’t actually do anything yet.

Pulling back and looking at the adapter metaphor in general, adapters are used to hook up two pipes or cords that don’t fit together in and of themselves. So you get thingamajig a with two sides: one that fits with your first pipe, and one that fits with your second. What we’ve done here is craft a TankAdapter part that fits onto the Vehicle “pipe.” We now need to make it fit on the Tank “pipe.”

To do that, we simply give the TankAdapter a Tank object and map the Vehicle methods to the appropriate Tank methods. In this case, it would look like this:

public class TankAdapter : Vehicle
{
    private Tank _wrappedTank;

    public TankAdapter(Tank wrappedTank)
    {
        _wrappedTank = wrappedTank;
    }

    public override void Start()
    {
        _wrappedTank.FireUpTheTank();
    }
}

And, there you have it. The adapter fits both “pipes,” and we had no need to change either one to make this happen. You’ve met your external requirements without compromising the design of the code that you do control. Now you can ship it.

A More Official Explanation

An implementation of the Adapter pattern is going to consist of a few basic entities. The Client is a class or piece of code that is using some Target. A desire to add to the functionality of the Target emerges, but the new class to be used, the Adaptee, is not compatible with the Client. The Client expects a different interface from the one that the Adaptee provides. So an Adapter is introduced that understands what the Client wants and what the Adaptee provides, and it maps Client desires to Adaptee functions.

In our example, VehicleSimulator is the Client, Vehicle is the Target, Tank is the Adaptee, and TankAdapter is the Adapter. VehcileSimulator wants to use Tank, but it can’t, given Tank’s interface. We explored the possibility of changing VehicleSimulator, but decided that the needed changes were simply too burdensome if there were another way. So, not wanting to change Client, and not being able to change Target or Adaptee, we introduced Adapter.

Other Quick Examples

  1. One place you may see this a lot is code that deals with a GUI. For instance, let’s say that you have a bunch of controls that you hide when something happens behind the scenes. Let’s also say you’ve inherited some kind of custom control that you now want to hide, but it doesn’t expose a “Visibility” property. A very common solution here would be to define your own custom control that wraps the existing one and figures out how to hide it.
  2. We’ve talked about this in the context of inheritance, but this also applies to interface polymorphism. Let’s say you have a series of classes that implement IDisposable and that is how you clean up after yourself in some presentation layer code–aggregate the controls and invoke their “Dispose()” methods. If you’re then offered a one-off that doesn’t implement IDisposable, you can define a class that does and wrap your one-off in it to keep with the pattern.
  3. The null object pattern is a very specific example of the Adapter pattern.

A Good Fit – When to Use

The Adapter pattern makes sense when you have two components that need to interact with one another and you’re limited or even prohibited in terms of your ability to change either component. This limitation may be the result of things that we’ve discussed, such as requirements, politics, and good design practices.

Square Peg, Round Hole – When Not to Use

The Adapter Pattern is a hack. Make no mistake about that. It’s a pretty hack, and it’s in the GOF book, but it’s a hack nonetheless. I would advise against using it when you have the ability to change the components that you’re contemplating adapting.

For example, if in our example, we had the ability to modify Tank.cs, it would have made a lot more sense to have it inherit from Vehicle. Even if legacy code depended on its method names, we could simply have defined “Start()” in Tank.cs that called “FireUpTheTank()”. Alternatively, if we had access to Vehicles.DLL, it might have made sense to define our own Tank class and port the functionality from Tank.cs.

In my estimation, either of those options is better than using the Adapter pattern. This mimics adapters in the pipe analogy too–if you bring in a plumber and he’s rebuilding a whole piping system from scratch, why bother cobbling a bunch of old pipes together when they aren’t designed for that? Better just to get the right pipe in the first place.

So What? Why is this Better?

The Adapter Pattern is a better alternative to littering some client code with a bunch of conditional logic. As you saw in the example above, introducing a one-off can create havoc in client code. In our example that maintained a parallel set of Vehicles and Tanks, imagine the maintenance on that class if Vehicle had many methods and many clients. You would have to do that doubling of effort each and every place any method or property on Vehicle was invoked.