Adapter
Your checkout calls payer.pay(rupees), but the old payment gateway offers make_payment(paise) and cannot be changed.
Write a small wrapper that implements the interface your code expects and, inside, translates each call into what the strange class understands.
You must use a class whose interface you cannot change
The picture from life
You travel from India to Europe with a three-pin charger and find a two-pin socket. You do not rebuild the hotel or the charger; you plug a small travel adapter between them. The charger sees the shape it knows, the wall sees the shape it knows.
The problem
What goes wrong when you do not use it.
Your checkout calls payer.pay(rupees), but the old payment gateway offers make_payment(paise) and cannot be changed. If you call the gateway directly, its odd shape leaks into every place that takes money.
- calls / uses
- You must use a class whose interface you cannot change
- You want one interface over several similar third-party libraries
- Legacy code must keep working with new code
The idea
How the pattern fixes it.
Write a small wrapper that implements the interface your code expects and, inside, translates each call into what the strange class understands.
- implements
- has a / holds
- calls / uses
Who is who
- Payer (target)
- The interface your code wants: pay(rupees).
- OldGateway (adaptee)
- The existing class with the different method.
- PaymentAdapter
- Implements Payer, holds an OldGateway and converts calls.
Build it in steps
- 1Write down the interface your code expects.
- 2Create the adapter implementing it.
- 3Hold the strange object inside the adapter.
- 4Translate arguments and results in each method.
Watch it run
Step through the calls. Tap an arrow to jump to it.
checkout uses the interface it knows: pay(rupees).
pay(5)The code
Python and Java. Try writing the classes yourself from the diagram first.
class OldGateway:
def make_payment(self, paise):
return f"paid {paise} paise"
class PaymentAdapter: # the interface the app expects: pay(rupees)
def __init__(self, gateway):
self.gateway = gateway
def pay(self, rupees):
return self.gateway.make_payment(int(rupees * 100))
def checkout(payer, rupees):
return payer.pay(rupees)
def demo():
return checkout(PaymentAdapter(OldGateway()), 5)What you gain
- Reuse old or foreign code without editing it
- Conversion lives in one place
What it costs
- One more layer to read
- If you own the code, changing the interface directly may be simpler
You have already met it in
- Arrays.asList adapts an array to a List
- InputStreamReader adapts bytes to characters
- Wrappers around payment or SMS vendors
Often mixed up with
Make it stick
Your app wants a Logger with log(text). Adapt a library whose class has write(level, message, timestamp) so the app code does not change.