The Adapter pattern lets classes with incompatible interfaces work together. The adapter implements the interface the client expects and translates every call to the adapted class. It is how you plug third-party libraries into your code without letting their shapes spread everywhere.

The problem: incompatible interfaces

Our checkout code wants gateway.pay(amount, card). A payment SDK offers create_charge(cents, currency, source) and returns a dict. An old bank API offers make_payment(euros, account_no) and returns "OK" or "FAIL". We cannot edit either.

python
class StripeLikeSdk:                  # third-party code, we cannot edit it
    def create_charge(self, cents, currency, source):
        return {"id": "ch_1", "status": "succeeded", "amount": cents}

class OldBankApi:                     # another vendor, another shape
    def make_payment(self, euros, account_no):
        return "OK" if euros > 0 else "FAIL"

def checkout(gateway, amount, card):
    print("paid" if gateway.pay(amount, card) else "declined")

try:
    checkout(StripeLikeSdk(), 12.5, "tok_1")
except AttributeError as e:
    print(e)
text
'StripeLikeSdk' object has no attribute 'pay'

Object adapter

An adapter holds the vendor object (composition) and implements our interface. It converts euros to cents, adds the currency and turns the vendor result into a bool. The client does not know which vendor stands behind pay.