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
Read the animation as text
- 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.
- An adapter implements YOUR interface and holds the SDK. Your code calls pay(12.50) as usual.
- The adapter converts the inputs — £12.50 → 1250 pence, currency "GBP" — and calls the SDK.
- It converts the output back: "OK" becomes True. Your code never knew the SDK existed.
- 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 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
- Write down the interface your code expects: pay(pounds) returning True or False.
- Write down what the other system offers: make_payment(pence, currency) returning 'OK' or an error code.
- Create an adapter that implements your interface and holds the other system.
- 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.
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.
-
Watch
Christopher Okhravi · free on YouTube
-
Read
Refactoring.Guru, free
Step 3 Practise
Try each exercise yourself first: on paper, out loud or in code. Then open the answer and compare it with yours.
-
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.
-
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.
textOur 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 -
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.
pythonfrom 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 -
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.