Design Patterns Adapter
The Adapter pattern connects two incompatible interfaces so they can work together. It acts as a translator — like a travel plug adapter that lets a European device plug into a US power socket. Neither the plug nor the socket changes; the adapter sits in between and makes the connection work.
This pattern belongs to the Structural category. Structural patterns control how classes and objects are assembled into larger structures.
The Everyday Plug Adapter Explained
You buy a laptop in Germany. The charger has a round two-pin European plug. Your hotel room has a flat three-pin US socket. You cannot force the plug in — the shapes are incompatible. A travel adapter accepts the European plug on one side and fits the US socket on the other. The laptop charges. Nothing was re-engineered.
Software adapters work identically. You have existing code (the plug) and a new system (the socket). You write an adapter class that translates calls from one to the other.
Visual Diagram: The Adapter Bridge
+------------------+ +------------------+ +---------------------+ | Client | | Adapter | | Adaptee (Existing) | | | | | | | | calls: |──────►| request() |──────►| specificRequest() | | target.request()| | translates and | | (does the work) | | | | calls adaptee | | | +------------------+ +------------------+ +---------------------+ Client knows only "request()". Adapter knows both sides and bridges the gap. Adaptee never changes.
Key Participants
Target Interface
The interface the client expects. The client only knows this interface and calls methods on it. It has no knowledge of the adaptee.
Adaptee
The existing class with useful behavior but an incompatible interface. You cannot or do not want to change this class — it may be a third-party library, a legacy system, or code you do not own.
Adapter
The translator class. It implements the Target interface so the client is happy, and internally it calls the Adaptee to do the actual work.
Client
The code that needs a service. It works with the Target interface and never touches the Adaptee directly.
Real Scenario: Integrating a Legacy Payment System
Your e-commerce app uses a PaymentProcessor interface with a pay(amount) method. A new vendor provides a LegacyPaymentGateway class that uses makePayment(dollars, cents) instead. You cannot rewrite the vendor's code.
WHAT YOU HAVE:
LegacyPaymentGateway
└── makePayment(dollars, cents)
WHAT YOUR APP EXPECTS:
PaymentProcessor
└── pay(amount)
THE PROBLEM:
App calls pay(19.99) → LegacyGateway has no pay() method → ERROR
THE ADAPTER SOLUTION:
LegacyPaymentAdapter implements PaymentProcessor
pay(amount):
dollars = int(amount)
cents = round((amount - dollars) * 100)
legacy_gateway.makePayment(dollars, cents)
App calls pay(19.99) → Adapter converts → makePayment(19, 99) →
Code Walkthrough in Python
Step 1 — The Target Interface
class PaymentProcessor:
def pay(self, amount):
raise NotImplementedError
Step 2 — The Adaptee (existing, incompatible class)
class LegacyPaymentGateway:
def make_payment(self, dollars, cents):
print(f"Legacy system charging ${dollars}.{cents:02d}")
Step 3 — The Adapter
class LegacyPaymentAdapter(PaymentProcessor):
def __init__(self, legacy_gateway):
self.gateway = legacy_gateway
def pay(self, amount):
dollars = int(amount)
cents = round((amount - dollars) * 100)
self.gateway.make_payment(dollars, cents)
Step 4 — The Client Uses It Normally
gateway = LegacyPaymentGateway() processor = LegacyPaymentAdapter(gateway) processor.pay(19.99) # Output: Legacy system charging $19.99
The client called pay(19.99) — the same method it uses for every other payment processor. The adapter handled all translation internally.
Two Types of Adapter
Object Adapter (Composition)
+------------------+ | Adapter | |------------------| | adaptee: Adaptee | ← holds a reference to adaptee |------------------| | request() | ← calls adaptee.specificRequest() +------------------+ Adapter CONTAINS the adaptee. Works in all languages. Most common approach.
Class Adapter (Inheritance)
+------------------+ | Adapter | | extends Target | | extends Adaptee | ← inherits from both |------------------| | request() | ← calls inherited specificRequest() +------------------+ Adapter INHERITS the adaptee. Only works in languages that support multiple inheritance (C++). Not available in Python (cleanly) or Java.
Object Adapter is the standard choice in most modern languages.
Diagram: Object Adapter vs Class Adapter
OBJECT ADAPTER CLASS ADAPTER
Client Client
│ │
▼ ▼
Target ◄──── Adapter Target ◄──── Adapter
│ │
│ holds reference │ inherits
▼ ▼
Adaptee Adaptee
When to Use the Adapter Pattern
Use Adapter When:
- You want to use an existing class but its interface does not match what you need.
- You integrate third-party libraries or legacy systems you cannot modify.
- You want several incompatible classes to work behind a single interface.
Avoid Adapter When:
- You control both sides of the interface — just change one to match the other directly.
- The gap between interfaces is so large that the adapter becomes its own complex system — that signals a design problem.
Adapter Pattern in the Real World of Software
SCENARIO ADAPTER SOLVES THIS
--------------------------------------------------------------
Reading XML data in a JSON app XmlToJsonAdapter wraps XML
reader, outputs JSON-like objects
Old SQL database + new ORM DatabaseAdapter maps ORM calls
to raw SQL queries
Multiple cloud storage APIs StorageAdapter wraps S3, Azure,
(S3, Azure, GCP) GCP behind one interface: upload()
Device drivers in an OS OS calls standard driver API;
adapter translates to hardware
manufacturer's specific commands
Adapter vs Similar Patterns
+-------------+----------------------------------------------+ | Pattern | Purpose | +-------------+----------------------------------------------+ | Adapter | Makes incompatible interfaces compatible | | Facade | Simplifies a complex system (one clean API) | | Decorator | Adds new behavior to an existing object | | Proxy | Controls access to an object | +-------------+----------------------------------------------+ Adapter changes the INTERFACE. Decorator changes the BEHAVIOR. Facade simplifies COMPLEXITY.
Advantages and Disadvantages
Advantages
- Reuses existing classes without modifying them — preserves working code.
- Separates interface-translation logic into one dedicated class.
- Lets different incompatible systems collaborate through a shared interface.
Disadvantages
- Adds an extra layer — more classes to read and maintain.
- If many adapters stack on top of each other, tracing a call becomes harder.
Quick Summary
ADAPTER PATTERN — AT A GLANCE
Problem: Two classes cannot work together because their
interfaces do not match.
Solution: Write an Adapter class that wraps the incompatible
class and translates calls.
Key idea: Adapter implements the interface the client expects;
internally it delegates to the adaptee.
Best for: Third-party integrations, legacy system upgrades,
multi-platform support.
The Adapter pattern keeps your existing code intact and your new code clean. When two systems need to talk but speak different languages, the adapter becomes the professional interpreter between them.
