Adapter means: wrap a class with one interface so it looks like a different interface your code already expects.
You keep both sides unchanged. The adapter sits in the middle and translates calls from the shape the caller uses into the shape the existing class actually understands.
Three roles show up every time:
Target — the interface your code already calls.
Adaptee — the existing class with a different, incompatible interface.
Adapter — the new class that implements Target and calls Adaptee underneath.
The problem it solves
Your code expects one interface for playing media. A third-party library exposes a different one, with different method names.
AdvancedMediaPlayer has no play(audioType, fileName) method. You cannot easily rewrite a library you do not own, and you do not want its method names spreading through your app. Adapter bridges the two without touching either.
The object adapter
The common form uses composition. The adapter holds the adaptee and implements the target interface, forwarding each call to the right method underneath.
public interface Target { void request();}public class Adaptee { public void specificRequest() { System.out.println("Adaptee specific request"); }}public class ClassAdapter extends Adaptee implements Target { @Override public void request() { specificRequest(); }}// useTarget target = new ClassAdapter();target.request(); // calls specificRequest() underneath
// Go has no inheritance, so there is no true class adapter.// Embedding gets you method reuse, but Request() still has to be// written by hand — which makes this the same shape as the object// adapter above.type Target interface { Request()}type Adaptee struct{}func (a Adaptee) SpecificRequest() { fmt.Println("Adaptee specific request")}type ClassAdapter struct { Adaptee}func (c ClassAdapter) Request() { c.SpecificRequest()}// usevar target Target = ClassAdapter{}target.Request()
This locks the adapter to one adaptee, chosen at compile time, and it inherits everything the adaptee exposes. Prefer the object adapter unless you have a specific reason to reach for inheritance.
Worked example: a legacy payment system
A legacy payment method with its own signature, sitting next to a newer interface your app now standardizes on.
Both processors are constructed and called the same way. One happens to route through an adapter and an old system underneath; the caller cannot tell.
When it helps
Reach for Adapter when you are integrating with something you cannot change — a third-party library, a legacy module, an external API — and its interface does not match what your code expects.
It also helps during a gradual migration: wrap the old implementation in an adapter that satisfies the new interface, ship the new interface everywhere, and swap the real implementation later without anyone noticing.
The signal to look for is a shape mismatch, not a missing feature. Adapter does not add capability — the adaptee could already do the job.
When it hurts
If you own both sides of the interface, just change the interface directly instead of adding a translation layer to maintain forever. A pile of adapters is often a sign an interface needs redesigning, not patching around.
Watch for adapters that quietly grow business logic. An adapter’s job is translation only. Once it starts validating input or branching on business rules, it has become a hidden layer of application logic wearing an adapter’s name.
Practical tips
Default to the object adapter (composition) over the class adapter (inheritance).
Keep each adapter focused on one adaptee and one target interface.
Name and document what is being adapted — a short comment saves the next reader from tracing two classes.
Do not confuse Adapter with Facade: Adapter fixes a mismatch between two interfaces; Facade simplifies access to a subsystem whose interfaces already work fine.
Check your understanding
What is Adapter, in one sentence?
A class that translates one interface into another so two otherwise incompatible pieces of code can work together unchanged.
Object adapter vs. class adapter?
Object adapter holds the adaptee as a field and uses composition; class adapter extends the adaptee and uses inheritance. Prefer the object adapter for its flexibility.
When should you reach for Adapter?
When you cannot, or should not, modify one side of a mismatched interface — typically a third-party library, a legacy class, or an external API.
What is the biggest misuse of Adapter?
Letting it accumulate business logic instead of staying a pure translation layer.
What to learn next
Next: Decorator Pattern — add optional behavior to an object without touching its class or creating a mountain of subclasses.