Як ми це можемо зробити?
Отже, маємо базовий клас Автомобіля (Car):
public class Car
{
protected String BrandName { get; set; }
public virtual void Go()
{
Console.WriteLine("I'm " + BrandName + " and I'm on my way...");
}
}
і конкретна імплементація - Mersedes:
public class Mersedes : Car
{
public Mersedes()
{
BrandName = "Mersedes";
}
}
Паттерн Декоратор використовується для надання деякої додаткової функціональності нашим об'єктам. В нашому прикладі, ми хочемо додати функціональність сирени до конкретної імплементації автомобіля, проте нам нічого не заважає причепити на машину газовий розпилювач абощо. Для того щоб зберегти контракт базового класу Car, і щоб мати базовий клас для всіх інших "прибамбасних" функціональностей створимо CarDecorator, що так само наслідується від Car:
public class DecoratorCar : Car
{
protected Car DecoratedCar { get; set; }
public DecoratorCar(Car decoratedCar)
{
DecoratedCar = decoratedCar;
}
public override void Go()
{
DecoratedCar.Go();
}
}
Як ми уже зауважили цей клас обгортує DecoratedCar. Це є причина чому цей патерн ще часто називають Wrapper.
Настав час для "прибамбасів". Ми додаємо додаткову функціональність до будь-якої машини (чи то мерс чи то беха), наслідуючися від CarDecorator класу.
public class AmbulanceCar : DecoratorCar
{
public AmbulanceCar(Car decoratedCar)
: base(decoratedCar)
{
}
public override void Go()
{
base.Go();
Console.WriteLine("... beep-beep-beeeeeep ...");
}
}
Тут ми додали простий екстеншин біпкання.
І власне те чого ми добивалися:
Викорисання вигладає дуже приємно - ми покриваємо Мерседс фічами Швидкої і отримуємо мрію лікаря (doctorsDream), опісля ми можемо покрити цей об'єкт ще єкимись штукенціми, і що важливо ми це все можемо робити динамічно. Сниться лікарю* біпкалка - передаємо його мерс у декоратор Ambulance, сниться йому кулемет на криші автомобіля, передаємо у декоратор Armor.
var doctorsDream = new AmbulanceCar(new Mersedes());
doctorsDream.Go();
Вивід:
I'm Mersedes and I'm on my way...
... beep-beep-beeeeeep ...
... beep-beep-beeeeeep ...
Надіюся що цей аутпут був очікуваний. А тепер швидко глянемо на UML цієї мудрості-чудасії:
Цей патерн має щось спільне із Композитом (Composite) і Адаптером (Adapter). Адаптер може змінити інтерфейс поведінки, а Декоратор ні (ми наслідуємося від Car). Композит працює із великою кількістю компонент, а не як декоратор тільки із однією.
Якщо вам сподобався виклад цього дизайн патерну, дайте мені знати.
Також Ви можете глянути на табличку із ДП на блозі автора тут.
(*) раніше було "сниться дікарю біпкалка", стало "сниться лікарю біпкалака".

Надзвичайно просто та зрозуміло. Розжували все, залишилося лише ковтнути.. :)
ВідповістиВидалитиОписав з фантазією, це плюс. "Сниться дікарю біпкалка" мене трохи поперло, думаю це чепятка ))
ВідповістиВидалитиА так все гуд, коротко та зрозуміло, так тримати.
Ігор, дякую, я справив. Мабуть то чепятка тому, що я вже сонний добре був :)
ВідповістиВидалитиГарно написав, thx
ВідповістиВидалитиПрошу, я стараюся :)
ВідповістиВидалитиАга, доречі на моєму блозі вже є декілька патернів описаних подібним або околоподібним стилем, але чомусь ніхто їх так високо не оцінює як ви, за винятком декількох друзів. (Може просто ніхто не читав ще :) )
ВідповістиВидалитиЯ правда не зрозумів для чого DecoratorCar має бути нащадком Car. Хтось пояснить доступно?:)
ВідповістиВидалитиДмитро, щоб зберегти той самий контракт. (Я це був вказав "Для того щоб зберегти контракт базового класу Car, і щоб мати базовий клас для всіх інших "прибамбасних" функціональностей створимо DecoratorCar, що так само наслідується від Car")
ВідповістиВидалитиНу може краще для прикладу було написати ICar тоді було б чіткіше ясно що то контракт.
Чи ти із мене просто прикалуєшся? :) Інакше б цей патерн не називався б враппер, якщо б він не мав тих самих методів, які він враппає.
ВідповістиВидалитиВсе-все, зрозумів...
ВідповістиВидалитиКласний паттерн. Один з моїх улюблених. І написано доступно, тільки мені здається що на UML діаграмі скорше зображений брідж ніж декоратор.
ВідповістиВидалитиТакож на мою думку якщо потрібний лише один декоратор то можна обійтися без базового класу (CarDecorator) і реалізувати зразу ж (AmbulanceCar).
Можна було обійтися тільки із одним декоратором. А на UML таки не брідж зображений, але певно ще бракує стрілки, щоб зобразити, що CarDecorator також наслідується від Car.
ВідповістиВидалити