Adapter Pattern belongs to the Structural category of design patterns.
Why? Because it helps you structure your code to connect incompatible interfaces without changing their existing code. In other words, “Make two systems shake hands even if they speak different languages.”
Let me explain with a real world analogy. Imagine traveling from India to Europe. Your laptop charger uses a 3-pin plug, but European sockets accept only 2-pin plugs. Do you throw away your laptop? Of course not!
You use a universal adapter to connect your plug to the socket. No change made to your charger, and definitely no change to the wall socket.
In code, the Adapter Pattern works the same way i.e. helping two incompatible systems work together without altering their existing structures.
Legacy Payment System vs New App
Let’s say you are building a new e-commerce app, but your company still uses an old legacy payment system and its interface looks something like this,
Class LegacyPaymentGateway
Method MakePayment(amountInCents)
Print("Payment done: " + amountInCents + " cents")
But your new app works with,
Interface INewPayment
Method Pay(amountInRupees)
If you compare the two, they have different methods, different parameter formats. Can we have a direct integration between these two systems? Not possible... right?
This is where the Adapter Pattern helps us.
What is the Adapter Pattern?
Adapter Pattern is a structural design pattern that allows incompatible systems to communicate by introducing a middle-layer adapter.
It’s like hiring a translator between two teams speaking different languages. Neither team needs to change how they speak. It is the translator (adapter) who handles the conversion.
Talking about connecting the New app to the Legacy payment system, what options do we have? Instead of rewriting either system, we build an Adapter class that connects the new app to the old system.
What does the code look like?
// Step 1: Target Interface (the expected standard in your new system)
Interface INewPayment
Method Pay(amountInRupees)
// Step 2: Legacy System (existing code you can’t modify)
Class LegacyPaymentGateway
Method MakePayment(amountInCents)
Print("Payment done: " + amountInCents + " cents")
// Step 3: The Adapter (The Universal Plug)
Class PaymentAdapter Implements INewPayment
Constructor(legacyGateway)
this.legacyGateway = legacyGateway
Method Pay(amountInRupees)
amountInCents = amountInRupees * 100
legacyGateway.MakePayment(amountInCents)
// caller logic
legacySystem = new LegacyPaymentGateway()
paymentGateway = new PaymentAdapter(legacySystem)
paymentGateway.Pay(250) // ₹250 payment done using legacy system!
What did we achieve?
- Your new system uses the modern interface (
INewPayment
). - Legacy system stays untouched following the
Open-Closed Principle
. - Adapter acts like the middleman doing the conversion.
- Code stays clean and future proof.
When should you use the Adapter Pattern?
- You have to work with legacy systems.
- Two systems have incompatible interfaces.
- Want to avoid changing existing working code.
- Need to reuse existing code without rewriting.
Use Cases?
- Payment gateway integrations
- Connecting new apps to old APIs
- File format conversions
- Database migration adapters
To summarize, it would be apt to say,
Adapter Pattern lets you connect mismatched systems without modifying them, by creating a middleman that speaks both languages.
It helps you make incompatible systems work together by avoiding the need to modify existing/legacy code. This keeps your code clean, structured, and extensible.
Hope this article helped you “plug in” the Adapter Pattern to your design knowledge.
Next up in the series: Singleton Pattern. Do give it a read...
Top comments (0)