“Патерн Вівторка” #2: Декоратор
|Розкажу я вам про лікаря, який мав хорошу й швидку машину – Mersedes. Оскільки він лікар Львівської лікарні, то по дорозі на роботу він часто застрягає у пробках, тому він спізнюється і це його виводить із себе, в результаті чого страждають… Хто? – Його пацієнти. В нашого лікаря є мрія, що його машина перетворилася на Швидку і всі інші автомобілі уступаються йому дорогу на роботу. Тільки одна проблема із цим: Швидка вміє сигналити сиреною, а його мерс не має такої можливості, нам, як програмістам, було б добре ДЕКОРУВАТИ (decorate) мерс, таким чином, щоб він почав голосно “вити”.
Як ми це можемо зробити?
Отже, маємо базовий клас Автомобіля (Car):
і конкретна імплементація – Mersedes:
Паттерн Декоратор використовується для надання деякої додаткової функціональності нашим об’єктам. В нашому прикладі, ми хочемо додати функціональність сирени до конкретної імплементації автомобіля, проте нам нічого не заважає причепити на машину газовий розпилювач абощо. Для того щоб зберегти контракт базового класу Car, і щоб мати базовий клас для всіх інших “прибамбасних” функціональностей створимо CarDecorator, що так само наслідується від Car:
Як ми уже зауважили цей клас обгортує DecoratedCar. Це є причина чому цей патерн ще часто називають Wrapper.
Настав час для “прибамбасів”. Ми додаємо додаткову функціональність до будь-якої машини (чи то мерс чи то беха), наслідуючися від CarDecorator класу.
Тут ми додали простий екстеншин біпкання.
І власне те чого ми добивалися:
Викорисання вигладає дуже приємно – ми покриваємо Мерседс фічами Швидкої і отримуємо мрію лікаря (doctorsDream), опісля ми можемо покрити цей об’єкт ще єкимись штукенціми, і що важливо ми це все можемо робити динамічно. Сниться лікарю* біпкалка – передаємо його мерс у декоратор Ambulance, сниться йому кулемет на криші автомобіля, передаємо у декоратор Armor.
Вивід:
… beep-beep-beeeeeep …
Надіюся що цей аутпут був очікуваний. А тепер швидко глянемо на UML цієї мудрості-чудасії:
Цей патерн має щось спільне із Композитом (Composite) і Адаптером (Adapter). Адаптер може змінити інтерфейс поведінки, а Декоратор ні (ми наслідуємося від Car). Композит працює із великою кількістю компонент, а не як декоратор тільки із однією.
Якщо вам сподобався виклад цього дизайн патерну, дайте мені знати.
Також Ви можете глянути на табличку із ДП на блозі автора тут.
(*) раніше було “сниться дікарю біпкалка”, стало “сниться лікарю біпкалака”.
Надзвичайно просто та зрозуміло. Розжували все, залишилося лише ковтнути.. 🙂
Описав з фантазією, це плюс. "Сниться дікарю біпкалка" мене трохи поперло, думаю це чепятка ))
А так все гуд, коротко та зрозуміло, так тримати.
Ігор, дякую, я справив. Мабуть то чепятка тому, що я вже сонний добре був 🙂
Гарно написав, thx
Прошу, я стараюся 🙂
Ага, доречі на моєму блозі вже є декілька патернів описаних подібним або околоподібним стилем, але чомусь ніхто їх так високо не оцінює як ви, за винятком декількох друзів. (Може просто ніхто не читав ще 🙂 )
Я правда не зрозумів для чого DecoratorCar має бути нащадком Car. Хтось пояснить доступно?:)
Дмитро, щоб зберегти той самий контракт. (Я це був вказав "Для того щоб зберегти контракт базового класу Car, і щоб мати базовий клас для всіх інших "прибамбасних" функціональностей створимо DecoratorCar, що так само наслідується від Car")
Ну може краще для прикладу було написати ICar тоді було б чіткіше ясно що то контракт.
Чи ти із мене просто прикалуєшся? 🙂 Інакше б цей патерн не називався б враппер, якщо б він не мав тих самих методів, які він враппає.
Все-все, зрозумів…
Класний паттерн. Один з моїх улюблених. І написано доступно, тільки мені здається що на UML діаграмі скорше зображений брідж ніж декоратор.
Також на мою думку якщо потрібний лише один декоратор то можна обійтися без базового класу (CarDecorator) і реалізувати зразу ж (AmbulanceCar).
Можна було обійтися тільки із одним декоратором. А на UML таки не брідж зображений, але певно ще бракує стрілки, щоб зобразити, що CarDecorator також наслідується від Car.