Introduction To The Adapter Pattern
The Adapter Design Pattern is a software design pattern that allows the interface of an existing class to be used from another interface. It's often used to make existing classes work with others without modifying their source code. The Adapter Pattern is especially useful when the classes that need to communicate with each other do not have compatible interfaces.
The Adapter Design Pattern can be implemented in two ways:
- Object Adapter Pattern: This pattern uses composition to adapt the interface of a specific object.
- Class Adapter Pattern: This pattern uses inheritance to adapt a class to another interface.
Classic Implementation
Let's focus on the Object Adapter Pattern as it's the more commonly used approach and TypeScript doesn't fully support classical inheritance as you would expect in other object-oriented languages.
Here's a simple example of an adapter in TypeScript:
Suppose we have a Rectangle
class:
class Rectangle {
constructor(private width: number, private height: number) {}
getWidth() {
return this.width;
}
getHeight() {
return this.height;
}
area() {
return this.width * this.height;
}
}
And we have another Square
class:
class Square {
constructor(private side: number) {}
getSide() {
return this.side;
}
area() {
return this.side * this.side;
}
}
As you can see, Square
and Rectangle
have different interfaces, so we can't
use them interchangeably directly.
Suppose we need to use Square
objects in a place where Rectangle
is
expected. We could create an adapter to make the Square
class work with the
code expecting a Rectangle
:
class SquareToRectangleAdapter {
constructor(private square: Square) {}
getWidth() {
return this.square.getSide();
}
getHeight() {
return this.square.getSide();
}
area() {
return this.square.area();
}
}
We can now use an instance of Square
where a Rectangle
is expected:
let square = new Square(5);
let adapter = new SquareToRectangleAdapter(square);
console.log(adapter.getWidth()); // 5
console.log(adapter.getHeight()); // 5
console.log(adapter.area()); // 25
This way, the SquareToRectangleAdapter
class adapts the interface of
the Square
class to the Rectangle
interface, allowing Square
objects to be
used where Rectangle
objects are expected.
When To Use Adapter Pattern
Incompatibility of interfaces: This is the most common scenario where the Adapter pattern is useful. When two parts of a system have different interfaces and need to communicate with each other, the Adapter pattern can help to "translate" between the two interfaces. This situation can arise often when integrating third-party libraries or APIs that don't directly fit into your application's design.
Refactoring of legacy code: In case of a system redesign where you want to maintain backward compatibility, the Adapter pattern can serve as a bridge between the new systems and the old ones. Instead of rewriting old interfaces, you can use an Adapter to match the new interfaces.
Alternatives to multiple inheritance: In languages that don't support multiple inheritance (like TypeScript or Java), the Adapter pattern can help to achieve similar goals. When a class needs to inherit behavior from multiple sources, adapters can provide an alternative way to reuse functionality.
Abstracting volatile classes: Sometimes you might need to abstract the use of volatile classes (ones that change often). The Adapter can encapsulate these changes so that the impact on the rest of the application is minimized.
Time To Transition From JavaScript To TypeScript
Level Up Your TypeScript And Object Oriented Programming Skills. The only complete TypeScript course on the marketplace you building TypeScript apps like a PRO.
SEE COURSE DETAILSWhat Can You Do Next 🙏😊
If you liked the article, consider subscribing to Cloudaffle, my YouTube Channel, where I keep posting in-depth tutorials and all edutainment stuff for ssoftware developers.