Imagine you are a professional tennis player who travel around the world for important tournaments but you are not able to speak any language other than English... So, in every non-english country you need someone to helps you with the speech. You might need to hire a professional interpreter to adapt your speech into the fan's country...
Given the above context, let's have a look at the definition of the Adapter Pattern:
"Adapter pattern allows objects with incompatible interfaces to work together".
And also, here is the Adapter Pattern class diagram:
Client: The component which is implemented against the target interface.
Target: The expected interface that the client invokes method on.
Adapter: The code that allows implement the interface that the client implements which has the ability to "talk" with the "adaptee" class.
Adaptee: is the library, component or system that we are not able to change.
Hiring a professional interpreter
Backing to the Profesional Tennis Player example; We have a player who is an english speaker and fans around the world (non-english speaker). As the player's fans doesn't speak english and we can not change the way that fans speaks we will use a "profesional interpreter" to translate the player's speech into the fan's language rather than learn every single language.
Before writing the code, let's look at the class diagram:
Greetings: A polite word or sign of welcome
TennisPlayer: Is the client who uses an english interface to say hello
SpanisInterpreterAdapter: Is the interpreter who can understand english and also, speak Spanish.
Spanish: Thats represent the language of the player's fans
Target
The IGreetings interface represents the "Target" node of the Adapter Pattern diagram. We have a simple interface with one method to say hello:
export interface IGreetings {
sayHello: () => void;
}
Adapter Class
The SpanishInterpreterAdapter class represents the "Adapter" node of the Adapter Pattern diagram. Also, this class implements the IGreetings interface because it is able to connect both incompatibles languages:
import { IGreetings } from "./greetings.interface";
import { Spanish } from "./spanish";
export class SpanishInterpreterAdapter implements IGreetings {
constructor(private spanish: Spanish) {}
sayHello(): void {
// the interpreter know's how to say hello in spanish
this.spanish.decirHola();
}
}
Adaptee Class
The Spanish class represents the "Adaptee" node of the Adapter Pattern diagram. We have a simple class with one method to say hello in spanish:
export class Spanish {
//sayHello means decirHola in Spanish
decirHola(): void {
console.log("Hola!");
}
}
Client
This is our Profesional Tennis Player which represents the "Client" node of the Adapter Pattern diagram:
import { Spanish } from "./src/spanish";
import { SpanishInterpreterAdapter } from "./src/spanish-interpreter.adapter";
// our professional tenis now is playing an important match in Spain
// we need to hire an interpreter to translate their speech from English to Spanish
const spanishInterpreterAdapter = new SpanishInterpreterAdapter(new Spanish());
spanishInterpreterAdapter.sayHello();
// Hola! (means hello in english)
Conclusions
Often we can get "Facade pattern" and "Adapter pattern" confused. The main difference between they is their purpose: "facade pattern" simplifies interactions with a complex subsystem, while "adapter pattern" helps to make incompatible interfaces work together.
The adapter pattern is used when you need to adapt an existing class to meet the needs of a new interface, rather than simplifying access to a complex subsystem.
I hope that this article was useful for improve your design patterns skills.
See you in the next one!