8.2. Adapter¶
EN: Adapter
PL: Adapter
Type: class and object
8.2.1. Use Cases¶
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
8.2.2. Problem¶
BlackAndWhite3rdPartyFilter
is from external libraryDoes not conform to
Filter
interfaceDo not have
apply()
methodNeed manual call of
init()
at initializationNeed manual call of
render()
from abc import ABCMeta, abstractmethod
from dataclasses import dataclass
class Image:
pass
class Filter(metaclass=ABCMeta):
@abstractmethod
def apply(self, image: Image) -> None:
pass
class VividFilter(Filter):
def apply(self, image: Image) -> None:
print('Applying Vivid Filter')
class BlackAndWhite3rdPartyFilter:
def init(self):
"""Required by 3rd party library"""
def render(self, image: Image):
print('Applying BlackAndWhite Filter')
@dataclass
class ImageView:
__image: Image
def apply(self, filter: Filter):
filter.apply(self.__image)
if __name__ == '__main__':
image_view = ImageView(Image())
image_view.apply(BlackAndWhite3rdPartyFilter())
# Traceback (most recent call last):
# AttributeError: 'BlackAndWhite3rdPartyFilter' object has no attribute 'apply'
8.2.3. Design¶
8.2.4. Implementation¶
Inheritance is simpler
Composition is more flexible
Favor Composition over Inheritance

Figure 8.3. Please mind, that on Picture there is a Caramel
filter but in code BlackAndWhite3rdPartyFilter
¶
Inheritance:
from abc import ABCMeta, abstractmethod from dataclasses import dataclass class Image: pass class Filter(metaclass=ABCMeta): @abstractmethod def apply(self, image: Image) -> None: pass class VividFilter(Filter): def apply(self, image: Image) -> None: print('Applying Vivid Filter') class BlackAndWhite3rdPartyFilter: def init(self): """Required by 3rd party library""" def render(self, image: Image): print('Applying BlackAndWhite Filter') @dataclass class BlackAndWhiteAdapter: __filter: BlackAndWhite3rdPartyFilter def apply(self, image: Image) -> None: self.__filter.init() self.__filter.render(image) @dataclass class ImageView: __image: Image def apply(self, filter: Filter): filter.apply(self.__image) if __name__ == '__main__': image_view = ImageView(Image()) image_view.apply(BlackAndWhiteAdapter(BlackAndWhite3rdPartyFilter()))
Composition:
from abc import ABCMeta, abstractmethod from dataclasses import dataclass class Image: pass class Filter(metaclass=ABCMeta): @abstractmethod def apply(self, image: Image) -> None: pass class VividFilter(Filter): def apply(self, image: Image) -> None: print('Applying Vivid Filter') class BlackAndWhite3rdPartyFilter: def init(self): """Required by 3rd party library""" def render(self, image: Image): print('Applying BlackAndWhite Filter') @dataclass class BlackAndWhiteAdapter(BlackAndWhite3rdPartyFilter, Filter): def apply(self, image: Image) -> None: self.init() self.render(image) @dataclass class ImageView: __image: Image def apply(self, filter: Filter): filter.apply(self.__image) if __name__ == '__main__': image_view = ImageView(Image()) image_view.apply(BlackAndWhiteAdapter())
8.2.5. 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]