Чим відрізняється декоратор від адаптера? (І про фасад)
Цих вихідних їхав поїздом додому, і в купе побачив на столі книжку “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()
Взагалі, це дуже часто застосовні адаптери, і їх не обов’язково знати щоб застосовувати. Але просто іноді варто знати значення цих слів, щоб розуміти про що люди пишуть.
Холера насправді не дорога)) Треба казати – “книга до холєри дорога”. Так моя бабця на Заході каже;-)
п.с. гарно пишеш, підчитую
Roman V. Slobodyan
5 Вересня, 2014 at 13:55
Дякую. 🙂
bunyk
6 Вересня, 2014 at 02:15