Skip to content
FollowtheDamnCode

Adapter

Wrap an incompatible class so it fits the interface your code already expects — no edits on either side.

Big idea

An Adapter is a thin wrapper that translates one interface into another. Your code keeps calling the interface it was designed around; the adapter converts each call into whatever the foreign class understands — neither side is modified.

The problem it solves

Your dashboard is built around a Thermometer interface with celsius(). You buy a third-party weather station whose SDK has get_reading() returning {"temp_f": 71.6, "ts": …}. You can't edit the SDK, and you don't want if isinstance(sensor, VendorStation) with Fahrenheit conversions sprinkled through the dashboard. Next year a second vendor arrives with yet another shape.

Think of it like

A travel plug adapter. Your laptop charger has a UK plug; the hotel wall has a European socket. You don't rewire the hotel or cut the plug off your charger — a small block in between makes each side see what it expects.

Structure

The adapter implements your interface and wraps the foreign class
  • Target (Thermometer) — the interface your client code already uses.
  • Adaptee (VendorStation) — the useful class with the wrong interface.
  • Adapter — implements the target, holds an adaptee, translates.

In code

A Fahrenheit station joins a Celsius dashboard
class LabProbe:                                   # already speaks our interface
    def __init__(self, c): self._c = c
    def celsius(self): return self._c


class VendorStation:                              # third-party: can't edit
    def get_reading(self):
        return {"temp_f": 71.6, "unit": "F"}


class VendorAdapter:                              # implements Thermometer, wraps the vendor
    def __init__(self, station):
        self._station = station
    def celsius(self):
        f = self._station.get_reading()["temp_f"]
        return round((f - 32) * 5 / 9, 1)


def show(sensors):                                # the client: unchanged
    for name, s in sensors.items():
        print(f"{name:8} {s.celsius():5.1f} C")


show({"lab": LabProbe(21.5), "roof": VendorAdapter(VendorStation())})
output
lab       21.5 C
roof      22.0 C

Run and edit it in the app.

The dashboard never learns that Fahrenheit exists. When a second vendor arrives, it gets its own adapter; the dashboard still doesn't change.

Watch it

Object adapter vs class adapter

  • Object adapter (above) — holds the adaptee; works with any subclass of it; the usual choice.
  • Class adapter — inherits from both target and adaptee (multiple inheritance). Less flexible, rarely worth it.
  • In Python, a function can be an adapter too: sorted(items, key=adapt) adapts items to "something comparable".

What does an Adapter change?

When to use it — and when not to

Use it to integrate third-party or legacy code you can't or shouldn't edit, to put several vendors behind one interface, or to keep a foreign API from leaking into your domain (an anti-corruption layer). If you own both sides and they're young, just change one of them — an adapter between two of your own fresh classes is a sign the interfaces should agree.

Pros and cons

ProsCons
Reuse code with a mismatched interfaceOne more class per adaptee
Client and adaptee stay untouched (Open/Closed)Translation can hide costs (a cheap-looking call that's a network round trip)
Isolates vendor quirks in one placeMismatched semantics can't always be adapted cleanly

How it relates

  • Decorator keeps the interface and adds behaviour; Adapter changes the interface and keeps behaviour.
  • Facade defines a new, simpler interface over many classes; Adapter makes one class fit an existing interface.
  • Proxy keeps exactly the same interface and controls access.
  • Bridge is designed up front to separate interface from implementation; Adapter is usually retrofitted.

You wrap a payment SDK's six classes behind one simple pay(amount) method your app defines. Adapter or Facade?

Key terms

  • Adapter — A wrapper that converts one interface into another that clients expect.
  • Target — The interface the client code is written against.
  • Adaptee — The existing class with a useful behaviour but an incompatible interface.
  • Anti-corruption layer — Adapters at a system boundary that keep a foreign model from leaking into yours.