The Adapter Pattern

The Adapter Pattern
Adapter can often be encountered in real-life.

Introduction

The adapter pattern is one of the classical 23 patterns described by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides in their high-impact book Design Patterns: Elements of Reusable Object-Oriented Software.

An adapter aims at solving a very common problem: changing the structure of a class while retaining its behavior. Gamma et al. also known as the Gang of Four define the adapter pattern as follows:

[Its intent is to] convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.

Gamma et al., 2003, p. 139

Since the adapter pattern changes the structure of a class it fits in the category of structural patterns together with the bridge, decorator, façade and proxy patterns which bear some resemblance to it.

Differentiation from other patterns

As such an adapter is to be differentiated from

  • A bridge which aims at decoupling an abstraction from its implementation. As such a bridge is a design decision taken before implementing anything, while the adapter is a way of gluing two classes/interfaces together after they were implemented.
  • A decorator which retains the interface of a class but changes or enhances its behavior. Since both the adapter and the decorator patterns are sometimes referred to as wrappers the risk of confusion is even greater.
  • A façade which offers a new simplified interface to an existing system hiding its complexities. The adapter pattern aims at translating an existing interface to another one.
  • A proxy which like the decorator also retains the interface of a class but acts as a replacement of initial class. Delegating to the replaced class happens only in specific cases: for example in the case of a proxy implementing access protection, as long as access is not granted, no delegation is performed.

Differentiating between patterns in real-life code can be sometimes difficult, because one pattern might be implemented through another one or multiple patterns might be used together.

Real-world examples of adapters

What makes the adapter pattern especially easy to understand compared to various other much more technical patterns is the prevalence of adapters in the real-world. Arguably everybody is familiar with the principles behind a power adapter or a ratchet.

Now faced with the problem of having to get two things talking to each other which weren’t designed to do so

  • you can either change one of the two things, i.e. adapt one of the interfaces,
  • or you create some sort of translator which mediates between them, i.e. create an adapter.

If you have both things under control, you could very well go with solution number one, but when you don’t own or control one of them, solution two might be the more appropriate answer. Changing the power sockets in your hotel room might consequently prove to be a bad idea.

Structure of the adapter pattern

The adapter pattern defines four roles:

  • The adaptee which is to be adapted to the target interface.
  • The adapter who performs the adaptation.
  • The target interface to be used by the client.
  • And the client who uses the adaptee through the adapter.

As already mentioned, the adapter pattern solves the problem of reusing the behavior of a class (or at least part of it) when this class does not offer the expected interface, and changing the interface is either not possible (you don’t own the code) or due to some reason undesirable (changing that particular interface might break other parts of the code). Basically this can be done in two ways:

  • By implementing an object adapter, the adapter delegating to an instance of the adaptee making thus use of composition.
UML diagram of the object adapter pattern.
  • By implementing a class adapter, the adapter inheriting the interfaces of the adaptees.
UML diagram of the class adapter pattern.

Whereas using an object adapter will always work, there are cases where a class adapter won’t work:

  • Whenever the adaptee is declared final and is thus not extendable.
  • In case your target is not an interface, but an abstract class and the language used does not support multiple inheritance.
  • In case you need adapting multiple adaptees through the adapter, again a limitation for languages which don’t support multiple inheritance.

Favoring the object adapter over the class adapter can also be reduced to the general rule of favoring composition over inheritance making thus the decision really simple.

Adapter pattern examples

I generally consider simple examples to be the best when discussing a design pattern, since they allow focusing on the essential aspects. As such let us consider the following setting: let’s say you would like to create a racing game. The player can either participate in car races, in motorcycle races or in mixed-vehicle races. You will have to implement the motorcycles, but luckily you already have the cars implemented from a previous project. Since you don’t want to change your old code, but still would like to handle both vehicle classes through the same interface you will have to adapt the cars to the newly created Vehicle interface. Let’s have a closer look at the setting.

Firstly the newly created Vehicle interface which provides all the basic functionality needed to handle motorized vehicles like accelerating, steering, honking and so on:

public interface Vehicle {
	public void accelerate();
	public void decelerate();
	// some more very fancy functionality
}

The Motorcycle implements the Vehicle interface since it is also newly created:

public class Motorcycle implements Vehicle {

	public void accelerate() {
		System.out.println("Motorcycle: accelerate");
	}
	
	public void decelerate() {
		System.out.println("Motorcycle: decelerate");
	}
	
	// some more very fancy functionality
}

The Car legacy class offers basically the same functionality as the Vehicle interface save some differently named methods. Differences in functionality are also possible. The legacy Car might lack the ability to honk; this would have to be implemented in the adapter class.

public class Car {

	public void increaseSpeed() {
		System.out.println("Car: increaseSpeed");
	}
	
	public void decreaseSpeed() {
		System.out.println("Car: decreaseSpeed");
	}
	
	// some more very fancy functionality
}

Since we want to use the Car as a Vehicle, we have to adapt it now to the newly created interface:

public class CarToVehicleAdapter implements Vehicle {

	private Car car;
	
	public CarToVehicleAdapter(Car car) {
		this.car = car;
	}
	
	@Override
	public void accelerate() {
		this.car.increaseSpeed();
	}

	@Override
	public void decelerate() {
		this.car.decreaseSpeed();
	}

}

This is basically it. The Car can be used as a Vehicle and the racing can begin:

public class RacingGame {

	public static final void main(String[] args) {
		Vehicle myLegacyCar = new CarToVehicleAdapter(new Car());
		Vehicle myFancyMotorcylce = new Motorcycle();
		
		myLegacyCar.accelerate();
		myFancyMotorcylce.accelerate();
		myLegacyCar.decelerate();
		myFancyMotorcylce.decelerate();
	}
}

And the result:

Car: increaseSpeed
Motorcycle: accelerate
Car: decreaseSpeed
Motorcycle: decelerate 

Class adapter pattern example

As you might have noticed the adapter is implemented as an object adapter. If you choose to implement it as a class adapter it would have to look as follows:

public class CarToVehicleClassAdapter extends Car implements Vehicle {

	@Override
	public void accelerate() {
		this.increaseSpeed();
	}

	@Override
	public void decelerate() {
		this.decreaseSpeed();
	}

}

Which leads, of course, to the same result:

public class RacingGame {

	public static final void main(String[] args) {
		Vehicle myLegacyCar = new CarToVehicleClassAdapter();
		Vehicle myFancyMotorcylce = new Motorcycle();
		
		myLegacyCar.accelerate();
		myFancyMotorcylce.accelerate();
		myLegacyCar.decelerate();
		myFancyMotorcylce.decelerate();
	}
}
Car: increaseSpeed
Motorcycle: accelerate
Car: decreaseSpeed
Motorcycle: decelerate 

An important question in the context of an adapter is how much work should it effectively do? The simple answer is: not more than it necessarily has to. The adapter is not intended for enhancing the functionality, unless this is necessary for the adaptation. In the simplest case the adapter will only delegate to the appropriate methods of the adaptee. As hinted above in the case of missing functionality for honking, the adapter could implement this. Alternatively, honking functionality could be added through subclassing or decorating the original Car, which would in turn be adapted to the Vehicle interface.

Real-life examples

An XmlAdapter is used in the JAXB-Framework for converting a bound type to a value type. While the name can be a hint for the function, this is no prerequisite. The AWT and Swing packages offer various so called adapter classes (for example WindowAdapter or MouseAdapter) which offer no-op implementations of the methods of multiple listener interfaces. While some consider them as examples of the class adapter pattern, these are no adapters in the sense of the Gang of Four definition.

InputStreamReader and OutputStreamWriter are also examples of classes adapting the behavior of InputStream respectively OutputStream to the Reader respectively the Writer classes.


References

Gamma, E., Helm, R., Johnson, R. & Vlissides, J. (2003). Design Patterns. Delhi, India: Pearson Education.


You might also like the following articles:

Petre Sora

Petre Soras Interessen sind vielfältig und befinden sich an der Schnittstelle zwischen Mensch und Informationstechnologie. Als studierter Psychologe und Software Engineer war er knappe sechs Jahre als Java-Entwickler in mehreren Unternehmen tätig. Mit der Gründung der Rezensionsplattform nososo hat er sich entschieden eigene Wege zu gehen. Petre ist als Rezensent und Verfasser von Artikeln für nososo tätig.

Dieser Beitrag hat 2 Kommentare

  1. Avatar
    Premps

    Great article, the analogy with the racing vehicle makes it even more easy to understand.

    Will there be maybe some mini projects attached to these technical articles for example on GitHub or somewhere else where we can have access to the source code?

    1. Petre Sora
      Petre Sora

      Thanks for your comment. I’m glad you liked the article. 🙂
      Versioning the examples somewhere publicly is a good idea. I’ll consider it for the future.

Schreibe einen Kommentar