Цих вихідних їхав поїздом додому, і в купе побачив на столі книжку “Heads first Design Patterns” видавництва O’reilly. Моїми сусідами по купе були хлопець з дівчиною. Вони виглядали сонними, і навіть плутали напрям поїздки (думали що поїзд до Києва їде, а не до Франківська, хоча їхали до Франківська), і активно проявляли намір лягти спати, тому я запитав:
- Можна я вашу книжку подивлюсь? Бо завжди хотів знати чим відрізняється декоратор від адаптера.
На що хлопець відповів:
- Так, звісно.
А дівчина:
- Ви що, теж програміст?! Як вас всіх скрізь багато розвелось!
Вони лягли на свої полиці, я теж підклав під голову рюкзак, вмостився на своїй полиці і взявся за книжку.
Книжка чудова, всім рекомендую. Правда, напевне дорога як холєра, але якщо вам пощастить до неї дістатись – читайте без жодних сумнівів. Щоб довго не тягнути інтригу, скажу що про відмінність декоратора і адаптера я таки дізнався. Там в цій книжці навіть цілий діалог між декоратором та адаптером описують, де ці два шаблони доводять один одному хто з них крутіший. :)
Отож, і декоратор і адаптер – шаблони обгортки. Вони при створенні екземпляра себе приймають якийсь об’єкт, і запам’ятовують його десь в своїх атрибутах. Ключова відмінність – декоратор має той самий тип що й огортуваний ним об’єкт, тобто інтерфейс об’єкта що декорується не змінюється, просто його функціональність розширюється, а адаптер – змінює інтерфейс зовсім.
Як приклад для декораторів наводили клас – напій.
# coding=utf-8
class Beverage(object):
def __init__(self,
description = 'Невідомий напій',
price = 0,
):
self._description = description
self._price = price
@property
def description(self):
return self._description
@property
def price(self):
return self._price
class Tea(Beverage):
def __init__(self,
description = 'Чай',
price = 2,
):
super(Tea, self).__init__(description, price)
class Coffee(Beverage):
def __init__(self,
description = 'Кава',
price = 3,
):
super(Coffee, self).__init__(description, price)
tea = Tea()
print tea.description, tea.price # чай 2
coffee = Coffee()
print coffee.description, coffee.price # кава 3
А тепер уявімо собі що нам треба ще написати класи для тих самих напоїв, але з цукром, а ще з молоком. Можна було б наслідуватись, але тоді в нас буде ціла купа різних класів. Цукор і молоко – це декоратори напою.
class BeverageDecorator(Beverage): # важливо що декорований напій, це теж напій
_description = 'Невідомий додаток'
_price = 0
def __init__(self, beverage):
self.beverage = beverage
@property
def price(self):
return self._price + self.beverage.price
@property
def description(self):
return self.beverage.description + ' і ' + self._description
class Sugar(BeverageDecorator):
_description = 'Цукор'
_price = 0.1
class Milk(BeverageDecorator):
_description = 'Молоко'
_price = 0.2
my_coffee = Milk(Sugar(Sugar(Coffee())))
print my_coffee.description, my_coffee.price
# Кава і Цукор і Цукор і Молоко 3.4
Бачимо, що декорований напій – все одно напій, хоча й з дещо зміненою функціональністю. Адаптери натомість можуть взяти один об’єкт і повернути цілком інший.
class FahrenheitThermometer(object):
@staticmethod
def get_temperature():
return 451
class CelsiusAdapter(object):
def __init__(self, fahrenheit_thermometer):
self.thermometer = fahrenheit_thermometer
def __call__(self):
return (self.thermometer.get_temperature() - 32) * 5.0 / 9
f = FahrenheitThermometer()
print f.get_temperature()
c = CelsiusAdapter(f)
print c()
Бачимо що адаптером ми можемо цілком поміняти інтерфейс нашого об’єкта, змусити його повертати інші дані, які запитуються іншим способом.
Багато із звичних декораторів в Python є насправді адаптерами. Тому можна сказати що в Python декоратор – це обгортка, яку крім того можна ще й особливим синтаксисом застосувати до огортуваного об’єкта. Той же @property
, @staticmethod
– змінюють інтерфейс виклику методу, роблячи його зручнішим. @twisted.internet.defer.inlineCallbacks
– теж змінює інтерфейс, перетворюючи співпрограму (coroutine) на набір асинхронних функцій.
Крім того, є особливий вид адаптера, який адаптує зразу кілька об’єктів, тільки називають його фасадом. Наприклад, нехай ми маємо такі об’єкти як вольтметр, та амперметр, і хочемо виміряти потужність нашої лампочки. Для цього треба послідовно під’єднати амперметр, паралельно вольтметр, і помножити їх показники. Це можна абстрагувати ватметром (в якого в реальності все одно чотири клеми, але не зациклюйтесь на реальності, ми тут моделі будуємо):
class Voltmeter(object):
def value(self):
return 220
class Ammeter(object):
def value(self):
return 0.36
class Wattmeter(object):
def __init__(self, voltmeter, ammeter):
self.voltmeter = voltmeter
self.ammeter = ammeter
def value(self):
return self.voltmeter.value() * self.ammeter.value()
w = Wattmeter(Voltmeter(), Ammeter())
print w.value()
Взагалі, це дуже часто застосовні адаптери, і їх не обов’язково знати щоб застосовувати. Але просто іноді варто знати значення цих слів, щоб розуміти про що люди пишуть.
Filed under:
Кодерство,
Нещоденник Tagged:
книжки,
розробка,
Python