Before you start
Adapter is a small pattern with a big role: it's the bridge between "code I own" and "code I don't". Almost every production system in the world uses it — usually without being called out by name, always doing the same job.
The one-line version
You have code that speaks interface A. You have to work with code that speaks interface B. Adapter is a class that IS-A "A" (from the caller's view) and HOLDS a "B" (which it delegates to inside).
When to use Adapter
Any time you see these signs:
- Shape mismatch across a boundary. Your code's shape differs from what's on the other side of a library / API / legacy service.
- Wrapping a third-party client. You want an interface YOUR app owns, so you can swap vendors later.
- Fake versions for tests. You want to swap a real database / HTTP client with a fake one. Each hides behind a shared interface.
- Multiple vendors, one shared contract. Twilio,
MessageBird, and AWS SNS all send SMS — one
SmsSenderinterface, one adapter per vendor.
Recognition trick: whenever a class in your code holds a reference to a "foreign" class and translates between them, you're probably looking at an Adapter.
Composition vs inheritance
Adapter has two flavors:
- Class adapter — subclass the thing you're adapting. Design C in the objective. Java historically used this because it has real multiple inheritance.
- Object adapter — hold the thing you're adapting as a field. Design D. Python's natural default.
Object adapter wins for Python because:
- Loose coupling. Adapter isn't tied to the whole API of the thing it wraps — just uses what it needs.
- Swappability. Multiple adapters for the same interface can each wrap a different thing.
- Testability. Faking the wrapped thing is easier when it's a field, not a parent class.
In this course, when we say "Adapter", assume object adapter unless we say otherwise.
Where Adapter shows up in design problems
- Payment gateway wrapping (each gateway is
different; a
PaymentGatewayAdapterper vendor lets you swap them). Overlaps with Abstract Factory when you have multiple related adapters per family. - Storage backends — S3, GCS, local disk each
wrapped behind a
Storageinterface. - Logging integrations — third-party logging
services (Sentry, DataDog, Splunk) each with a
different API wrapped behind an internal
Loggerinterface. - Notification channels — SendGrid, Twilio, Slack
each behind a
Notifierinterface. - Legacy database wrappers — old ORM's connection object wrapped to match a modern async interface.
Adapter vs Facade (previewing the next lesson)
Two patterns that both wrap other code. Different jobs:
- Adapter — makes an incompatible thing look compatible. One-to-one shape translation.
- Facade — hides a complex system behind a simple entry point. Many-to-one, and the underlying pieces don't need to be "wrong" in shape.
If you're translating shape, it's Adapter. If you're simplifying, it's Facade.
The interview cliché to avoid
Interviewers hate this answer: "It adapts the interface." Yes, we know. Give them what they actually want to hear:
- WHY you'd use Adapter over other options (rewriting the vendor, subclassing, inlining)
- WHERE you've used it in similar problems
- The composition-over-inheritance choice and WHY
Specific answers show experience. Definitions show memorization.
The decision cheat-sheet
| Situation | Answer |
|---|---|
| Third-party API's shape doesn't match yours | Adapter |
| Multiple vendors, same conceptual operation | Adapter (per vendor) + a factory |
| One-off translation, one call site | Just inline it |
| You control both sides | Make the interfaces match, no adapter |
| Complex subsystem needs one simple entry point | Facade (next lesson) |
| You want to add behavior AROUND an existing object | Decorator (lesson 8) |
| You want a stand-in that controls access | Proxy (lesson 9) |
Adapter, Facade, Decorator, and Proxy all wrap other objects — this table is your recognition guide.