Introduction
The Adapter pattern is used to make one interface compatible with another. It allows objects with different, and therefore incompatible interface to work together.
If you for example want to bring an existing class into your system with an interface that doesn’t match any existing interface, and you can not modify this class, you write an adapter class to bridge the gap.
It looks like this:

The pattern consists of different parts:
- The client has an object which implements the Subject interface.
- The object in question is the Adapter.
- The implementation of the operation() method in the Adapter wraps the concrete_operation in the RealSubject class.
That way we can talk to the RealSubject class using an already known interface. The Adapter class takes care of converting our method-calls for the RealSubject. Also, because this pattern is loosely coupled, changing the RealSubject should be relatively painless.
What is the difference between the Decorator and the Adapter pattern?
| Decorator | Adapter | |
| Purpose | The decorator pattern is used to add behaviour to objects dynamically without modifying the source. | Make one interface compatible with another |
| Use | This pattern is used to when you want to add or extend behaviour to existing classes in a flexible and reusable way without creating a hierarchy of subclasses. | Used to integrate an existing class with an interface which is not compatible with existing interfaces. Modifying this class is not needed if you wrap it in an Adapter. |
| Structure | Creating a decorator pattern involves creating a set of decorator classes that implement the same interface as the component, thereby extending or modifying its behaviour | Create an Adapter class, implementing the desired interface. This class also holds an instance of the class you want to adapted, the adaptee. The adapter delegates calls to the adaptee converting or transforming the input or output of these calls. |
| Example | Imagine having a bakery, you add things like GlazingDecorator or a WhippedCreamDecorator wrapping around your Pie-class to produces pies of the desired kind | A classic example would be the conversion of units of measurement. If you have a classes which expects lengths to be in feet and inches, yet your client needs meters, you can wrap the classes in ConverterAdapter and have the conversion done automatically |
Difference between Decorator and Adapter
In short, Adapter is used to make incompatible things work together, while the Decorator adds and extends functionality.
Implementation in Python
Preparing the stage
We will start by importing the following:
from abc import ABC, abstractmethod
Line by line:
- ABC stands for ‘Abstract Base Class’. When you derive from this class you simply tell Python that your class is meant to be abstract. This means that it is designed to be a template or a blueprint which should function as a baseclass, but should have no concrete implementation or instance of its own.
- The
@abstractmethoddecorator marks specific methods as abstract. This means that must and should be implemented by any concrete (i.e. non-abstract) subclass.
The target interface
We start by defining the interface of our target, that is the interface which our clients expect:
class Target(ABC):
@abstractmethod
def request(self) -> str:
pass
Line by line:
- The
Targetinterface derives fromABCso it is an abstract base class. - It defines one abstract method called
request().
The legacy service
To demonstrate the idea of an adapter, let’s define some LegacyService with a different interface:
class LegacyService:
def specific_request(self, data: str) -> str:
return f"LegacyService processing: {data}"
As you can see the interface of the LegacyService is different from our target.
The adapter
Since this interface is different, we need an adapter:
class ServiceAdapter(Target):
def __init__(self, legacy_service: LegacyService):
self._legacy_service = legacy_service
def request(self) -> str:
return self._legacy_service.specific_request("adapted data")
Line by line:
- The
ServiceAdapterinherits from theTargetinterface, which means it must implement therequest()method. - The constructor receives an object of type
LegacyServiceas its argument. - The
request()method basically redirects a call torequest()to thespecific_request()method on theLegacyService
The client code
All we now need is a small function to call our request() method:
def client_code(target: Target):
return target.request()
Time to test
We can now test our adapter:
if __name__ == "__main__":
legacy = LegacyService()
adapter = ServiceAdapter(legacy)
print(client_code(adapter))
What happens here is:
- We create our legacy service.
- We wrap it in our adapter,
- And call the desired functionality.
Conclusion
In conclusion, the Adapter pattern stands out as an indispensable tool in the object-oriented design toolkit, primarily serving as a vital bridge for resolving interface incompatibilities. Its function is elegantly simple yet profoundly impactful: enabling existing, potentially unmodifiable classes (the adaptee, or LegacyService in our example) to fulfill the contract of a client’s expected interface (the Target).
The Python implementation effectively showcased the mechanics of this process, utilizing inheritance to conform to the Target and delegation to translate the method call for the LegacyService. This deliberate loose coupling is its greatest strength, promoting extensive code reuse and simplifying the integration of external dependencies or complex legacy systems without requiring disruptive internal modifications. The Adapter shields the client from the underlying complexity of the original service, resulting in code that is more stable and maintainable over time.
Furthermore, understanding the Adapter pattern is incomplete without clearly distinguishing it from the Decorator pattern. While the Decorator is dedicated to the dynamic addition or extension of responsibilities, the Adapter’s mission is purely one of transformation and interoperability. By encapsulating the conversion logic within a dedicated adapter class, we ensure that the client code remains clean and ignorant of the interface friction beneath it. This strategic isolation solidifies the Adapter pattern’s status as a cornerstone of flexible and resilient software architecture.




