7.2. Adapter

  • EN: Adapter

  • PL: Adapter

  • Type: class and object

7.2.1. 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

  • Niekompatybilne API dwóch systemów

  • Wymagające różnych sposobów uwierzytelniania (OAuth2, BasicAuth)

  • Tłumaczenie pomiędzy różnymi formatami danych (SOAP/XML, REST/JSON)

  • Iteracyjne przepisywanie legacy systemu na nowy, ale tak, aby móc wciąż korzystać ze starego

design-patterns/structural/img/designpatterns-adapter-pattern.png

7.2.2. Problem

  • BlackAndWhite3rdPartyFilter is from external library

  • Does not conform to Filter interface

  • Do not have apply() method

  • Need manual call of init() at initialization

  • Need manual call of render()

design-patterns/structural/img/designpatterns-adapter-problem.png

from abc import ABC, abstractmethod
from pathlib import Path


#%% 3rd Party
class AmazingFilter:
    def __init__(self):
        print('Setting up filter')

    def transform(self, content: bytes) -> bytes:
        print('Making your phot amazing')
        return content


#%% Abstract
class Filter(ABC):
    @abstractmethod
    def apply(self, content: bytes) -> bytes:
        ...


#%% Implementation
class SepiaFilter(Filter):
    def apply(self, content: bytes) -> bytes:
        print('Making photo in sepia')
        return content

class SharpenFilter(Filter):
    def apply(self, content: bytes) -> bytes:
        print('Making photo sharp')
        return content


#%% Main
class Image:
    path: Path
    content: bytes

    def __init__(self, path):
        self.path = Path(path)
        self.content = self.path.read_bytes()

    def apply(self, filter: Filter) -> None:
        self.content = filter.apply(self.content)


if __name__ == '__main__':
    img = Image('/tmp/myfile.png')
    img.apply(SepiaFilter())
    img.apply(SharpenFilter())

    img.apply(AmazingFilter())
    # AttributeError: 'AmazingFilter' object has no attribute 'apply'

7.2.3. Solution

  • Inheritance is simpler

  • Composition is more flexible

  • Favor Composition over Inheritance

../../_images/designpatterns-adapter-solution.png

Figure 7.9. Please mind, that on Picture there is a Caramel filter but in code BlackAndWhite3rdPartyFilter


from abc import ABC, abstractmethod
from pathlib import Path


#%% 3rd Party
class AmazingFilter:
    def __init__(self):
        print('Setting up filter')

    def transform(self, content: bytes) -> bytes:
        print('Making your phot amazing')
        return content


#%% Abstract
class Filter(ABC):
    @abstractmethod
    def apply(self, content: bytes) -> bytes:
        ...


#%% Implementation
class SepiaFilter(Filter):
    def apply(self, content: bytes) -> bytes:
        print('Making photo in sepia')
        return content

class SharpenFilter(Filter):
    def apply(self, content: bytes) -> bytes:
        print('Making photo sharp')
        return content


# %% Adapter
class AmazingFilterAdapter(Filter):
    filter: AmazingFilter

    def __init__(self):
        self.filter = AmazingFilter()

    def apply(self, content: bytes) -> bytes:
        self.filter.__init__()
        return self.filter.transform(content)


#%% Main
class Image:
    path: Path
    content: bytes

    def __init__(self, path):
        self.path = Path(path)
        self.content = self.path.read_bytes()

    def apply(self, filter: Filter) -> None:
        self.content = filter.apply(self.content)


if __name__ == '__main__':
    img = Image('/tmp/myfile.png')
    img.apply(SepiaFilter())
    img.apply(SharpenFilter())
    img.apply(AmazingFilterAdapter())

7.2.4. Use Case - 0x01

>>> def otherrange(a, b, c):  # function with bad API
...     current = a
...     result = []
...     while current < b:
...         result.append(current)
...         current += c
...     return result
>>>
>>>
>>> def myrange(start, stop, step):  # adapter
...     return otherrange(a=start, b=stop, c=step)
>>>
>>>
>>> myrange(start=10, stop=20, step=2)
[10, 12, 14, 16, 18]

7.2.5. Assignments