Your company has a LegacyXMLParser that's been in production for six years. Dozens of other systems call parser.parseXML(data) exactly as it is — rewriting it is out of the question.
A brand-new module you're building, however, was designed around a clean JSONParser interface, with a parseJSON(data) method. Both are correct in their own world. Neither can talk to the other.
You can't change the legacy class. You don't want to redesign the new module around old, XML-shaped code either. Something needs to sit between them — that's an Adapter.
The pain: two interfaces, one problem
class LegacyXMLParser:
def parse_xml(self, data):
... # returns XML-shaped data, used by many existing systems
class JSONParser: # the interface the new module expects
def parse_json(self, data):
raise NotImplementedErrorThe new module only knows how to call parse_json(). The only working parser you have calls it parse_xml() and returns a different shape entirely. Editing LegacyXMLParser risks every existing caller; editing the new module to understand XML throws away the clean design you just built it with.
The pattern: a translator in between
Write one small class that implements the interface the new code expects, but internally calls the old class it actually has:
class XMLToJSONAdapter(JSONParser):
def __init__(self, legacy_parser: LegacyXMLParser):
self.legacy_parser = legacy_parser # holds the adaptee
def parse_json(self, data):
xml_result = self.legacy_parser.parse_xml(data)
return convert_xml_to_json(xml_result) # translate the shape
# usage — the new module never knows XML exists:
adapter = XMLToJSONAdapter(LegacyXMLParser())
result = adapter.parse_json(incoming_data)LegacyXMLParser is untouched — every old caller keeps working. The new module gets exactly the JSONParser interface it was designed against. The adapter is the only new piece, and its only job is translation.
Think of a power-plug adapter: it doesn't change the plug or the socket, it just sits between two things that were never designed to fit together.
Object Adapter vs Class Adapter
The version above — holding the adaptee as a field — is called an Object Adapter (composition). It works in any object-oriented language and is by far the more common choice.
A Class Adapter instead inherits from the adaptee directly, which requires multiple inheritance — not available for classes in languages like Java, so this variant is rarer in interview settings. When in doubt, default to the Object Adapter shape shown above.
Adapter vs Facade — don't mix these up
Both "wrap" something, but for different reasons:
- Adapter exists because a client expects a specific interface, and you have something that doesn't match it — the goal is compatibility.
- Facade exists to hide a complex subsystem (many classes, many calls) behind one simpler interface, with no particular pre-existing interface it's required to match — the goal is simplicity.
If the requirement is "make X work where Y is expected," that's Adapter. If it's "this subsystem has twelve classes and callers just want one simple entry point," that's Facade.
When Adapter isn't needed
Common mistakes
- Modifying the legacy/adaptee class instead of wrapping it — defeats the entire point.
- Confusing Adapter (interface compatibility) with Facade (simplifying a complex subsystem).
- Building a Class Adapter in a language without multiple inheritance for classes, when an Object Adapter would have worked fine.
- Adding an Adapter layer for brand-new code that has no real legacy or third-party constraint.
Quick recap
| Idea | One-liner |
|---|---|
| Pain solved | An existing interface doesn't match what a client expects, and you can't change either one. |
| Object Adapter | Wraps the adaptee as a field (composition) — the common, language-agnostic choice. |
| Class Adapter | Inherits from the adaptee — needs multiple inheritance, rarer in practice. |
| vs Facade | Adapter = match a specific expected interface. Facade = simplify a complex subsystem. |
| Overkill case | Brand-new code with no legacy/third-party interface to bridge. |
One thing to remember
Adapter never changes the two things that don't fit — it just adds one small translator in between, so both keep working exactly as they already do.
Practice Zone
Five MCQs, then two reasoning questions.
What problem does the Adapter pattern solve?
Asked in


What's the difference between an Object Adapter and a Class Adapter?
Asked in

How is Adapter different from Facade, since both 'wrap' something?
Asked in

Which requirement phrase is the strongest Adapter signal?
Asked in


You're writing a brand-new module from scratch, with no legacy code and no third-party library to integrate. Do you need an Adapter?
Asked in

Think it through, then reveal:
Your app has a working LegacyXMLParser (with a parseXML(data) method) used by older code across the company. A new module you're building expects any data source to implement a JSONParser interface with a parseJSON(data) method. You can't rewrite LegacyXMLParser — too many other systems depend on it exactly as it is. How does Adapter solve this?
Asked in


What's the actual downside of using an Adapter, even when it's the right pattern?
Asked in

FAQ
Is Adapter only useful for legacy code?
Legacy code is the most common real-world case, but the same pain shows up with third-party libraries you don't control (an SDK with its own interface shape) and even between two modules within the same codebase that were built by different teams with different conventions.
Can one Adapter class adapt more than one adaptee?
Usually not cleanly — an Adapter is built around one specific incompatible interface. If you need to adapt several different legacy sources to the same target interface, you typically write one adapter class per source, all implementing the same target interface (which itself is a small Strategy-like shape).
Does Adapter add runtime overhead?
Only one extra method call and any translation logic inside it — usually negligible compared to the benefit of not rewriting or duplicating a working legacy system.
You now have all five patterns this course focuses on. Before we design full systems with them, let's make sure you can actually draw what you've been reading — Lesson 8: UML Diagrams for LLD →


