Adapter

Adapter

  • The Adapter design pattern converts the interface of a class into another interface clients expect.
  • It's a structural design pattern

Use Cases of adapter pattern

  • Convert an interface of an object to a different form
  • Like power socket adapter for US and EU
  • Refactoring of a large application
  • Working with legacy code / database

Example implementation of adapter pattern

Let's we have two address classes VendorAddress and CustomerAddress as below.

from abc import ABC

class AbsAddress(ABC):
    line: str
    city: str
    country: str
    pin: str

class VendorAddress:
    def __init__(self, line1, line2, line3, city, country, pin):
        self.line1 = line1
        self.line2 = line2
        self.line3 = line3
        self.city = city
        self.country = country
        self.pin = pin

class CustomerAddress(AbsAddress):
    def __init__(self, line, city, country, pin):
        self.line = line
        self.city = city
        self.country = country
        self.pin = pin

But, the client only knows details about the abstract class. so, client implemented the below code.

def print_address(address):
    print(f'{address.line}, {address.city}, {address.country}, {address.pin}')

If we pass VendorAddress address to it then it will throw an error. so, we need an adapter which mimics the abstract class AbsAddress.

let's write the class VendorAddressAdapter

class VendorAddressAdapter:
    def __init__(self, vendor_address):
        self.line = f'{vendor_address.line1}, {vendor_address.line2}, {vendor_address.line3}'
        self.city = vendor_address.city
        self.country = vendor_address.country
        self.pin = vendor_address.pin

If we create adapter objects and pass it to the client funciton it will work. Let's put all components together

from abc import ABC

class AbsAddress(ABC):
    line: str
    city: str
    country: str
    pin: str

class VendorAddress:
    def __init__(self, line1, line2, line3, city, country, pin):
        self.line1 = line1
        self.line2 = line2
        self.line3 = line3
        self.city = city
        self.country = country
        self.pin = pin

class CustomerAddress(AbsAddress):
    def __init__(self, line, city, country, pin):
        self.line = line
        self.city = city
        self.country = country
        self.pin = pin

class VendorAddressAdapter:
    def __init__(self, vendor_address):
        self.line = f'{vendor_address.line1}, {vendor_address.line2}, {vendor_address.line3}'
        self.city = vendor_address.city
        self.country = vendor_address.country
        self.pin = vendor_address.pin


# client
def print_address(address):
    print(f'{address.line}, {address.city}, {address.country}, {address.pin}')


if __name__ == '__main__':
    address1 = CustomerAddress("101/HG", "Goldberg", "Sambala", 99099090)
    address2 = VendorAddress("105", "HG", "Hydro", "Mercury", "Gondwana", 88088080)
    address2_adapt = VendorAddressAdapter(address2)

    for address in [address1, address2_adapt]:
        print_address(address)

In the future, if we get more classes like this then we don't need to change the client implementation. we can have an adapter to solve it.

References: