Adapter Pattern
Concept
Category: Structural Pattern
The Adapter Pattern allows objects with incompatible interfaces to collaborate.
It acts as a literal “translator” sitting perfectly between two systems that refuse to speak the same language.
The Mental Model
You live in the United States and have a laptop with a standard US 2-prong plug.
You travel to the UK. The wall socket in your hotel is a massive, weird 3-prong UK socket.
You absolutely cannot jam your laptop plug into the wall.
What do you do? You buy an Adapter.
The Adapter has a UK plug on one side (to fit into the wall), and a US socket on the other side (so your laptop can plug into it). The electricity flows flawlessly between the two incompatible systems.
In software, imagine your application generates pure XML data.
You want to use a brilliant 3rd-party charting library to draw a graph. But the library ONLY accepts JSON data.
You cannot change your entire application to use JSON. You cannot change the 3rd-party library code.
You build an XMLToJsonAdapter!
Implementation
// 1. Our existing system (Generates XML)
class LegacySystem {
public getXMLData(): string {
return "<data><user>Alice</user><age>30</age></data>";
}
}
// 2. The 3rd-Party Library (STRICTLY requires JSON!)
interface AnalyticsLibrary {
analyze(jsonData: string): void;
}
class ModernAnalytics implements AnalyticsLibrary {
public analyze(jsonData: string): void {
console.log(`Rendering charts using JSON: ${jsonData}`);
}
}
// 3. The Adapter!
// It MUST implement the interface that the 3rd-party library expects (AnalyticsLibrary)
class XMLToJsonAdapter implements AnalyticsLibrary {
private adaptee: LegacySystem;
// It "wraps" our legacy system
constructor(adaptee: LegacySystem) {
this.adaptee = adaptee;
}
// It fulfills the 3rd-party contract, but translates the data inside!
public analyze(jsonData: string): void {
// 1. Fetch the incompatible XML data
const xml = this.adaptee.getXMLData();
// 2. Translate it! (Mock translation for example)
console.log(`[Adapter] Translating XML to JSON...`);
const json = `{ "user": "Alice", "age": 30 }`;
// 3. We cannot actually use the 'jsonData' parameter because our
// Legacy system is the true source of truth. We just needed to satisfy
// the interface so we could hook into the system!
// Pass the translated data to the modern library
const modernLib = new ModernAnalytics();
modernLib.analyze(json);
}
}
// Usage:
const oldSystem = new LegacySystem();
// The adapter disguises the old system as a modern AnalyticsLibrary
const adapter: AnalyticsLibrary = new XMLToJsonAdapter(oldSystem);
// The client code just calls the standard method, completely unaware
// that a translation is happening under the hood!
adapter.analyze("ignore_this");
Adapter vs Decorator vs Facade
These three Structural patterns all involve “wrapping” an object. How do you tell them apart?
- Adapter: Wraps an object to CHANGE its interface. (Makes incompatible things compatible).
- Decorator: Wraps an object to ADD new behaviors, while strictly maintaining the EXACT SAME interface. (Nesting dolls).
- Facade: Wraps an entire massive subsystem of multiple objects to SIMPLIFY the interface. (Hiding the spaghetti).
Interview Questions
Q: What is a “Two-Way Adapter”?
A: A standard Adapter only translates in one direction (XML to JSON). A Two-Way Adapter implements BOTH interfaces! It can take XML and feed it to the JSON library, AND it can take JSON and feed it back to the XML legacy system. This allows both systems to seamlessly send and receive data as if they were natively compatible.
Q: If I rewrite a 3rd-party library to accept XML instead of building an Adapter, is that better?
A: No, this is an architectural disaster. If you modify the source code of node_modules/chart-js, the absolute next time you run npm update, NPM will overwrite your modified file and instantly destroy your entire application. You must treat 3rd-party libraries as immutable black boxes. Adapters are the only safe way to bridge the gap without touching the source code.