The Adapter Design Pattern: Making Incompatible Code Work Together
Master the Adapter Design Pattern in Java. Learn how to integrate legacy code and 3rd-party libraries using the 'Travel Adapter' concept.
Moshiour Rahman
Advertisement
The Problem: The Square Peg in a Round Hole
Imagine you have a modern application that expects data in JSON format.
interface ModernClient {
void process(String jsonData);
}
But you need to integrate a legacy 3rd-party library (maybe an old banking system) that only outputs XML.
// You can't change this code (it's a library)
class LegacyBankSystem {
public String getAccountDataXML() {
return "<account><id>123</id></account>";
}
}
The interfaces don’t match. You can’t rewrite the legacy library, and you don’t want to pollute your modern app with XML parsing logic everywhere.
The Solution: The Adapter Pattern
The Adapter Pattern allows objects with incompatible interfaces to collaborate. It acts as a bridge (or wrapper) between two objects.
Real-Life Analogy: The Travel Adapter 🔌
- You (The Client): Have a US laptop plug (Type A).
- The Wall (The Service): Is a European socket (Type C).
- The Adapter: A small device that takes your US plug and fits it into the European socket.
It doesn’t change the electricity; it just adapts the interface.
Visualizing the Pattern

Implementation
1. The Target Interface (What your app expects)
public interface ModernPaymentGateway {
void pay(String currency, double amount);
}
2. The Adaptee (The incompatible 3rd party)
// Legacy code we can't change
public class LegacyPayPal {
// Note different method name and parameter order
public void sendPayment(double amount, String currencyCode) {
System.out.println("Processing PayPal: " + amount + " " + currencyCode);
}
}
3. The Adapter (The Bridge)
public class PayPalAdapter implements ModernPaymentGateway {
private final LegacyPayPal output;
public PayPalAdapter(LegacyPayPal output) {
this.output = output;
}
@Override
public void pay(String currency, double amount) {
// 1. Convert input if necessary (e.g., currency codes)
// 2. Delegate to the legacy method (Adapt the call)
output.sendPayment(amount, currency);
}
}
Usage
public class Shop {
private ModernPaymentGateway gateway;
public Shop(ModernPaymentGateway gateway) {
this.gateway = gateway;
}
public void checkout() {
// The shop only knows about ModernPaymentGateway
// It doesn't know it's actually using LegacyPayPal underneath
gateway.pay("USD", 50.00);
}
}
// In your setup code:
LegacyPayPal oldSystem = new LegacyPayPal();
ModernPaymentGateway adapter = new PayPalAdapter(oldSystem);
Shop shop = new Shop(adapter);
shop.checkout();
In The Wild (Real World Examples)
1. java.util.Arrays.asList()
This adapts an Array (Adaptee) to the List interface (Target).
String[] array = {"A", "B"};
List<String> list = Arrays.asList(array);
2. InputStreamReader
Adapts an InputStream (byte-oriented) to a Reader (character-oriented). It acts as a bridge between byte streams and character streams.
InputStream stream = new FileInputStream("file.txt");
Reader reader = new InputStreamReader(stream); // Adapter!
Cheat Sheet
| Feature | Details |
|---|---|
| Category | Structural |
| Problem Solved | Incompatible interfaces, integrations |
| Key implementation | Class implements Target and wraps Adaptee |
| Pros | Single Responsibility Principle (Data conversion logic lives in adapter), allows reuse of legacy code |
| Cons | Adds complexity of raw data conversion |
Tips to Remember 🧠
- “Wrapper”: The Adapter pattern is also called “Wrapper”.
- Legacy Code: Whenever you touch legacy code or 3rd party libraries, think Adapter. Isolate the ugliness inside the Adapter so your main code stays clean.
Advertisement
Moshiour Rahman
Software Architect & AI Engineer
Enterprise software architect with deep expertise in financial systems, distributed architecture, and AI-powered applications. Building large-scale systems at Fortune 500 companies. Specializing in LLM orchestration, multi-agent systems, and cloud-native solutions. I share battle-tested patterns from real enterprise projects.
Related Articles
The Visitor Design Pattern: Add Operations Without Modifying Classes
Master the Visitor Pattern in Java. Learn how to add new operations to object structures using double dispatch.
JavaThe Template Method Pattern: The Recipe for Success
Master the Template Method Pattern in Java. Learn how to define the skeleton of an algorithm in a superclass but let subclasses override specific steps.
JavaThe Strategy Design Pattern: Kill the If-Else Statements
Master the Strategy Pattern in Java. Learn how to replace complex if-else logic with interchangeable algorithms and follow the Open/Closed Principle.
Comments
Comments are powered by GitHub Discussions.
Configure Giscus at giscus.app to enable comments.