Phase 6 · Design patterns · Topic 6.4

Adapter

Must know

Lets two incompatible interfaces work together, by wrapping one so it looks like the other.

Not started

Step 1 Understand it

The problem it solves. You often need a library or old system whose doesn't match the one your code expects, and you can't (or shouldn't) change either side.

New words in this topic (tap one to see what it means)

Watch it happen

AnimationAdapter: translate between the interface you want and the one you have
Read the animation as text
  1. Your code expects pay(amount_in_pounds) → True/False. A third-party bank SDK offers make_payment(pence, currency) → "OK" or an error code. They don't fit.
  2. An adapter implements YOUR interface and holds the SDK. Your code calls pay(12.50) as usual.
  3. The adapter converts the inputs — £12.50 → 1250 pence, currency "GBP" — and calls the SDK.
  4. It converts the output back: "OK" becomes True. Your code never knew the SDK existed.
  5. An error code like "E42" becomes a proper exception — PaymentDeclined — rather than being silently swallowed.

Picture it

flowchart LR
  C["Our checkout code"] -->|"pay(20.00)"| A["LegacyBankAdapter"]
  A -->|"make_payment(2000, 'GBP')"| L["LegacyBankSDK"]
  L -->|"'OK'"| A
  A -->|"True"| C
The translates in both directions: pounds into pence on the way in, 'OK' into True on the way out.

The idea

Your code expects pay(amount_in_pounds), but a third-party offers make_payment(pence, currency). An adapter wraps the SDK and translates the calls, so your code doesn't change.

How it works, step by step

  1. Write down the interface your code expects: pay(pounds) returning True or False.
  2. Write down what the other system offers: make_payment(pence, currency) returning 'OK' or an error code.
  3. Create an adapter that implements your interface and holds the other system.
  4. In each method, convert the inputs (pounds → pence), call the other system, and convert the outputs (codes → True/False, or proper exceptions).

Analogy

A UK-to-EU travel plug adapter.

Where the analogy breaks

A plug adapter only changes the shape. Software adapters often also convert data (units, formats, errors), and a careless conversion can hide real problems.

Example

Wrapping an old XML-based bank service so it fits your new PaymentGateway interface.

Must-know points

  • Use it to plug in third-party or legacy code without changing your own interface.
  • Prefer wrapping an object (object adapter) over from it (class adapter).
  • Keeps vendor-specific code in one place, so switching vendors is easy.

Common mistakes

  • Letting the third-party's types leak through the adapter into business code.
  • Losing error details during the conversion.
python
class PaymentGateway:                      # what our code expects
    def pay(self, pounds: float) -> bool: ...

class LegacyBankSDK:                        # what we were given
    def make_payment(self, pence: int, currency: str) -> str:
        return "OK"

class LegacyBankAdapter(PaymentGateway):
    def __init__(self, sdk: LegacyBankSDK):
        self.sdk = sdk
    def pay(self, pounds):
        return self.sdk.make_payment(round(pounds * 100), "GBP") == "OK"

Step 2 Go further (free, optional)

Optional. The notes above are written to be enough on their own. These free resources give a second explanation and extra practice.

Step 3 Practise

Try each exercise yourself first: on paper, out loud or in code. Then open the answer and compare it with yours.

  1. ExplainSay it in plain words in under a minute, with no notes.

    Explain Adapter with the travel-plug picture.

    Show answer

    You bring a UK hair dryer to Spain. The dryer expects a UK socket; the wall has an EU socket. You don't rewire the hotel, and you don't take the dryer apart: you plug a travel adapter in between. It changes the shape of the connection so the two fit, and neither side has to change.

    The Adapter pattern is the same in code. Your code expects an interface (pay(pounds)); a third-party SDK you can't change offers something different (make_payment(pence, currency)). An adapter class implements your interface and translates each call into the SDK's form: pounds into pence, a "GBP" argument added, the SDK's "OK" string turned into True. Your code and the SDK both stay exactly as they are.

  2. DrawSketch the diagram (or class diagram) from memory.

    Draw PaymentGateway, LegacyBankAdapter and LegacyBankSDK, with arrows.

    Show answer

    Our code depends only on PaymentGateway. The adapter implements that interface (dotted line) and holds the SDK it wraps (solid arrow), translating every call. Swapping to a different bank later means writing a different adapter; our code never changes.

    text
    Our checkout code
       │ pay(12.50)
       ▼
    «interface» PaymentGateway
     + pay(pounds) → bool
       △
       ┊ implements
    LegacyBankAdapter
     - sdk
     + pay(pounds):
         make_payment(1250, "GBP") == "OK"
       │ wraps and calls
       ▼
    LegacyBankSDK   (we can't change this)
     + make_payment(pence, currency) → str
  3. ApplyUse it in a real design question and say exactly where it fits.

    Write an adapter so that a weather returning Fahrenheit fits an interface that returns Celsius.

    Show answer

    Our code wants current_celsius(city name). The third-party offers get_temp(city code) in Fahrenheit. The adapter translates both ways: the city name into the API's code on the way in, and Fahrenheit into Celsius ((F − 32) × 5 ÷ 9) on the way out. Nothing that uses TemperatureSource knows the API exists.

    python
    from abc import ABC, abstractmethod
    
    
    class TemperatureSource(ABC):                    # what our code expects
        @abstractmethod
        def current_celsius(self, city) -> float: ...
    
    
    class AcmeWeatherAPI:                             # the third-party client we were given
        def get_temp(self, city_code):
            return {"LDN": 59.0, "NYC": 86.0}[city_code]      # Fahrenheit
    
    
    class AcmeWeatherAdapter(TemperatureSource):
        CODES = {"London": "LDN", "New York": "NYC"}
    
        def __init__(self, api):
            self.api = api
    
        def current_celsius(self, city):
            fahrenheit = self.api.get_temp(self.CODES[city])
            return round((fahrenheit - 32) * 5 / 9, 1)
    
    
    weather = AcmeWeatherAdapter(AcmeWeatherAPI())
    print(weather.current_celsius("London"), weather.current_celsius("New York"))   # 15.0 30.0
  4. Trade-offsSay what it costs, when not to use it, and the alternative.

    What can go wrong if an adapter silently converts or swallows errors?

    Show answer

    An adapter sits exactly where two systems meet, so anything it hides, nobody else sees.

    Silent conversion:
    • Lost : £10.005 rounded to pence, or rounded twice in different places, and the totals stop matching the bank's.
    • Wrong assumptions: a timestamp assumed to be UTC that was local time, or a unit assumed to be metres that was feet. Everything "works" and the numbers are quietly wrong.
    • Missing data filled with defaults: a missing temperature becomes 0 °C, which is a real temperature.

    Swallowed errors:
    • The SDK says "DECLINED" in one case and "TIMEOUT" in another, and the adapter returns False for both. The caller can't tell "the card was refused, don't " from "we don't know, check before retrying", and may charge twice.
    • An exception is caught and inside the adapter, so the caller thinks the payment failed when it went through (or the opposite).

    A good adapter converts explicitly and with tests, and translates errors into your own error types that keep their meaning (PaymentDeclined, PaymentTimedOut), instead of hiding them.

Step 4 Interview-ready, then prove it

Your interview answer

About 20 to 40 seconds, said out loud. Practise it until it sounds like you, not like notes.

Adapter wraps an incompatible class so it matches the interface my code expects — for example making an old bank SDK fit our PaymentGateway interface — so business code doesn't change and vendors can be swapped.

Follow-up questions

Adapter or ?

An adapter makes one interface match another that's expected. A facade gives a simpler interface over a complicated set of classes.

Tick each check when you can do it without notes

A topic is finished when you pass all four checks, not when you finish a video. Be strict: if you needed your notes, it is not ticked yet.

  • Explain Say it in plain words in under a minute, with no notes.
  • Draw Sketch the diagram (or class diagram) from memory.
  • Apply Use it in a real design question and say exactly where it fits.
  • Trade-offs Say what it costs, when not to use it, and the alternative.

0 of 4 checks ticked.

Saved in this browser. Back it up or move it to another device.