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.