Декоратор (фр. décorateur) — структурний шаблон проєктування, призначений для динамічного підключення додаткових можливостей до об'єкта. Шаблон Decorator надає гнучку альтернативу методу визначення підкласів з метою розширення функціональності.
Основні характеристики
Завдання
Об'єкт, який передбачається використовувати, виконує основні функції. Проте може виникнути потреба додати до нього деяку додаткову функціональність, яка виконуватиметься до і/або після основної функціональності об'єкта.
Спосіб вирішення
Декоратор передбачає розширення функціональності об'єкта без визначення підкласів.
Учасники
Клас ConcreteComponent
— клас, в який за допомогою шаблону Декоратор додається нова функціональність. В деяких випадках базова функціональність надається класами, похідними від класу ConcreteComponent
. У подібних випадках клас ConcreteComponent
є вже не абстрактним, а конкретним. Абстрактний клас Component
визначає інтерфейс для використання всіх цих класів.
Переваги
- Декоратори забезпечують гнучку альтернативу підкласу для розширення функціональності
- Декоратори дозволяють модифікувати поведінку під час виконання, а не повертатися до існуючого коду та вносити зміни
- Декоратори - це хороше рішення для перестановки завдань, тому що ви можете загорнути компонент з будь-якою кількістю декораторів
- Шаблон декоратора підтримує принцип, що класи повинні бути відкриті для розширення, але закриті для модифікації
Недоліки
- Декоратори можуть призвести до багатьох невеликих об'єктів у нашому дизайні, і надмірне використання може бути складним
- Декоратори можуть викликати проблеми, якщо клієнт сильно залежить від компонентів конкретного типу
- Декоратори можуть ускладнити процес аналізу компонента, оскільки вам потрібно не лише інвентувати компонент, але і обернути його кількома декораторами
- Може бути складно, щоб декоратори відслідковували інших декораторів, тому що повертатися назад до декількох шарів ланцюга декораторів починає натискати шаблон декоратора поза його справжнім наміром
Наслідки
Функціональність, що додається, реалізується в невеликих об'єктах. Перевага полягає в можливості динамічно додавати цю функціональність до або після основної функціональності об'єкта ConcreteComponent
.
Зв'язок з іншими патернами
- Стратегія змінює реалізацію, декоратор — доповнює
- Ланцюжок обов’язків та Декоратор виконують операції через серію пов’язаних об’єктів. Але Ланцюжок обов’язків може виконувати довільні дії, незалежні одна від одної, а також у будь-який момент переривати виконання, а декоратори розширюють певну дію, не ламаючи інтерфейс базової операції і не перериваючи виконання інших декораторів.
Реалізація
Створюється абстрактний клас, що представляє як початковий клас, так і нові функції, що додаються в клас. У класах-декораторах нові функції викликаються в необхідній послідовності — до або після виклику подальшого об'єкта.
C++
#include <iostream> using namespace std; //абстрактний клас - основа патерну //декорації підлягає метод do_it() struct I { virtual ~I() {} virtual void do_it() = 0; }; //"справжній" клас - його метод do_it() мав працювати безумовно struct A : public I { ~A() { cout << "A dtor" << '\n'; } virtual void do_it() { cout << 'A'; } }; //ще один абстрактний клас - основа майбутніх декорацій-обгорток class D : public I { private: I* m_wrappee; public: //декоратор приховує всередині обгорнутий "справжній" об'єкт //і перенаправляє йому запити щось зробити D(I* inner) : m_wrappee(inner) {} ~D() { delete m_wrappee; } virtual void do_it() { m_wrappee->do_it(); } }; // конкретні реалізації обгорток: спочатку працює вкладений об'єкт, потім - обгортка struct X : public D { X(I* core) : D(core) {} ~X() { cout << "X dtor" << " "; } virtual void do_it() { D::do_it(); cout << 'X'; } }; struct Y : public D { Y(I* core) : D(core) {} ~Y() { cout << "Y dtor" << " "; } virtual void do_it() { D::do_it(); cout << 'Y'; } }; struct Z : public D { Z(I* core) : D(core) {} ~Z() { cout << "Z dtor" << " "; } virtual void do_it() { D::do_it(); cout << 'Z'; } }; void main() { I* anXYZ = new Z(new Y(new X(new A))); anXYZ->do_it(); cout << '\n'; // A X Y Z delete anXYZ; A a; I* anX = new X(&a); anX->do_it(); cout << '\n'; // A X }
C#
using System; namespace Decorator { // основа патерну interface INotifier { void Send(); } // "справжній" клас // надсилає сповіщення користувачу class UserNotifier : INotifier { public void Send() { Console.WriteLine("Notify user regularly"); } } // абстрактний клас декорацій abstract class NotifierDecoratorBase : INotifier { // декоратор приховує всередині обгорнутий "справжній" об'єкт protected INotifier notifier; public NotifierDecoratorBase(INotifier notifier) { this.notifier = notifier; } // перенаправляє "справжньому" об'єкту запити щось зробити public virtual void Send() { notifier.Send(); } } // різноманітні декорації // додаткові алгоритми надсилання сповіщень class SmsNotifier : NotifierDecoratorBase { public SmsNotifier(INotifier notifier) : base(notifier) { } public override void Send() { // спочатку працює вкладений об'єкт, потім - обгортка base.Send(); Console.WriteLine("Notify user with sms"); } } class EmailNotifier : NotifierDecoratorBase { public EmailNotifier(INotifier notifier) : base(notifier) { } public override void Send() { base.Send(); Console.WriteLine("Notify user with email"); } } class Program { // конфігурації системи static bool isSmsNotificationEnabled = true; static bool isEmailNotificationEnabled = false; static void Main(string[] args) { // створюємо спосіб оповіщення INotifier notifier = new UserNotifier(); // обгортаємо його залежно від налаштувань систему if (isSmsNotificationEnabled) notifier = new SmsNotifier(notifier); if (isEmailNotificationEnabled) notifier = new EmailNotifier(notifier); // виконуємо дію notifier.Send(); Console.ReadLine(); } } }
Java
import java.util.List; import java.util.ArrayList; import java.util.Collections; abstract class WordSplitter { abstract public List<String> getWords(String wordString); } /** Просто перетворює рядок слів, написаних через пробіл, на список слів */ class PureWordSplitter extends WordSplitter { public PureWordSplitter() {} public List<String> getWords(String wordString) { String[] wordArray = wordString.split(" "); List<String> wordList = new ArrayList<String>(wordArray.length); Collections.addAll(wordList, wordArray); return wordList; } } /** Абстрактний клас для декораторів */ abstract class FilteringSplitter extends WordSplitter { protected WordSplitter decoratedWordSplitter; abstract public List<String> getWords(String wordString); } /** Декоратор, який видаляє слова з не більше ніж двома буквами */ class ShortWordsFilteringSplitter extends FilteringSplitter { public ShortWordsFilteringSplitter(WordSplitter wordSplitter) { decoratedWordSplitter = wordSplitter; } public List<String> getWords(String wordString) { List<String> wordList = decoratedWordSplitter.getWords(wordString); for (int i = 0; i < wordList.size();) { if (wordList.get(i).length() <= 2) { wordList.remove(i); } else { ++i; } } return wordList; } } /** Декоратор, який видаляє зі списку слова, що починаються з великої букви */ class PropperNamesFilteringSplitter extends FilteringSplitter { public PropperNamesFilteringSplitter(WordSplitter wordSplitter) { decoratedWordSplitter = wordSplitter; } public List<String> getWords(String wordString) { List<String> wordList = decoratedWordSplitter.getWords(wordString); for (int i = 0; i < wordList.size();) { String word = wordList.get(i); if (word.isEmpty() || Character.isUpperCase(word.charAt(0))) { wordList.remove(i); } else { ++i; } } return wordList; } } class Main { public static void main(String[] args) { WordSplitter wordSplitter = new PropperNamesFilteringSplitter(new ShortWordsFilteringSplitter(new PureWordSplitter())); List<String> result = wordSplitter.getWords("no hi Afrika yes ambitious come Ukraine Ua"); for (String word : result) { System.out.print(word + " "); } } }
Зауваження і коментарі
- Хоча об'єкт-декоратор може додавати свою функціональність до або після функціональності основного об'єкта, ланцюжок створюваних об'єктів завжди повинен закінчуватися об'єктом класу
ConcreteComponent
. - Базові класи мови Java широко використовують шаблон Декоратор для організації обробки операцій введення-виведення.
Посилання
- Decorator design pattern [ 14 жовтня 2007 у Wayback Machine.]
- Design Patterns: Elements of Reusable Object-Oriented Software [ 9 листопада 2012 у Wayback Machine.]
- - Дизайн-патерни — просто, як двері
- Чим відрізняється декоратор від адаптера? (І про фасад) [ 22 грудня 2014 у Wayback Machine.] - Блог одного кібера
Література
Алан Шаллоуей, Джеймс Р. Тротт. Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М. : «Вильямс», 2002. — 288 с. — .
Вікіпедія, Українська, Україна, книга, книги, бібліотека, стаття, читати, завантажити, безкоштовно, безкоштовно завантажити, mp3, відео, mp4, 3gp, jpg, jpeg, gif, png, малюнок, музика, пісня, фільм, книга, гра, ігри, мобільний, телефон, android, ios, apple, мобільний телефон, samsung, iphone, xiomi, xiaomi, redmi, honor, oppo, nokia, sonya, mi, ПК, web, Інтернет
U Vikipediyi ye statti pro inshi znachennya cogo termina Dekorator znachennya Dekorator fr decorateur strukturnij shablon proyektuvannya priznachenij dlya dinamichnogo pidklyuchennya dodatkovih mozhlivostej do ob yekta Shablon Decorator nadaye gnuchku alternativu metodu viznachennya pidklasiv z metoyu rozshirennya funkcionalnosti Osnovni harakteristikiZavdannya Ob yekt yakij peredbachayetsya vikoristovuvati vikonuye osnovni funkciyi Prote mozhe viniknuti potreba dodati do nogo deyaku dodatkovu funkcionalnist yaka vikonuvatimetsya do i abo pislya osnovnoyi funkcionalnosti ob yekta Sposib virishennya Dekorator peredbachaye rozshirennya funkcionalnosti ob yekta bez viznachennya pidklasiv Uchasniki Klas ConcreteComponent klas v yakij za dopomogoyu shablonu Dekorator dodayetsya nova funkcionalnist V deyakih vipadkah bazova funkcionalnist nadayetsya klasami pohidnimi vid klasu ConcreteComponent U podibnih vipadkah klas i ConcreteComponent i ye vzhe ne abstraktnim a konkretnim Abstraktnij klas i Component i viznachaye interfejs dlya vikoristannya vsih cih klasiv Perevagi Dekoratori zabezpechuyut gnuchku alternativu pidklasu dlya rozshirennya funkcionalnosti Dekoratori dozvolyayut modifikuvati povedinku pid chas vikonannya a ne povertatisya do isnuyuchogo kodu ta vnositi zmini Dekoratori ce horoshe rishennya dlya perestanovki zavdan tomu sho vi mozhete zagornuti komponent z bud yakoyu kilkistyu dekoratoriv Shablon dekoratora pidtrimuye princip sho klasi povinni buti vidkriti dlya rozshirennya ale zakriti dlya modifikaciyi Nedoliki Dekoratori mozhut prizvesti do bagatoh nevelikih ob yektiv u nashomu dizajni i nadmirne vikoristannya mozhe buti skladnim Dekoratori mozhut viklikati problemi yaksho kliyent silno zalezhit vid komponentiv konkretnogo tipu Dekoratori mozhut uskladniti proces analizu komponenta oskilki vam potribno ne lishe inventuvati komponent ale i obernuti jogo kilkoma dekoratorami Mozhe buti skladno shob dekoratori vidslidkovuvali inshih dekoratoriv tomu sho povertatisya nazad do dekilkoh shariv lancyuga dekoratoriv pochinaye natiskati shablon dekoratora poza jogo spravzhnim namirom Naslidki Funkcionalnist sho dodayetsya realizuyetsya v nevelikih ob yektah Perevaga polyagaye v mozhlivosti dinamichno dodavati cyu funkcionalnist do abo pislya osnovnoyi funkcionalnosti ob yekta ConcreteComponent Zv yazok z inshimi paternami Strategiya zminyuye realizaciyu dekorator dopovnyuye Lancyuzhok obov yazkiv ta Dekorator vikonuyut operaciyi cherez seriyu pov yazanih ob yektiv Ale Lancyuzhok obov yazkiv mozhe vikonuvati dovilni diyi nezalezhni odna vid odnoyi a takozh u bud yakij moment pererivati vikonannya a dekoratori rozshiryuyut pevnu diyu ne lamayuchi interfejs bazovoyi operaciyi i ne pererivayuchi vikonannya inshih dekoratoriv Realizaciya Stvoryuyetsya abstraktnij klas sho predstavlyaye yak pochatkovij klas tak i novi funkciyi sho dodayutsya v klas U klasah dekoratorah novi funkciyi viklikayutsya v neobhidnij poslidovnosti do abo pislya vikliku podalshogo ob yekta C Priklad realizaciyi movoyu S include lt iostream gt using namespace std abstraktnij klas osnova paternu dekoraciyi pidlyagaye metod do it struct I virtual I virtual void do it 0 spravzhnij klas jogo metod do it mav pracyuvati bezumovno struct A public I A cout lt lt A dtor lt lt n virtual void do it cout lt lt A she odin abstraktnij klas osnova majbutnih dekoracij obgortok class D public I private I m wrappee public dekorator prihovuye vseredini obgornutij spravzhnij ob yekt i perenapravlyaye jomu zapiti shos zrobiti D I inner m wrappee inner D delete m wrappee virtual void do it m wrappee gt do it konkretni realizaciyi obgortok spochatku pracyuye vkladenij ob yekt potim obgortka struct X public D X I core D core X cout lt lt X dtor lt lt virtual void do it D do it cout lt lt X struct Y public D Y I core D core Y cout lt lt Y dtor lt lt virtual void do it D do it cout lt lt Y struct Z public D Z I core D core Z cout lt lt Z dtor lt lt virtual void do it D do it cout lt lt Z void main I anXYZ new Z new Y new X new A anXYZ gt do it cout lt lt n A X Y Z delete anXYZ A a I anX new X amp a anX gt do it cout lt lt n A X C Priklad realizaciyi movoyu S using System namespace Decorator osnova paternu interface INotifier void Send spravzhnij klas nadsilaye spovishennya koristuvachu class UserNotifier INotifier public void Send Console WriteLine Notify user regularly abstraktnij klas dekoracij abstract class NotifierDecoratorBase INotifier dekorator prihovuye vseredini obgornutij spravzhnij ob yekt protected INotifier notifier public NotifierDecoratorBase INotifier notifier this notifier notifier perenapravlyaye spravzhnomu ob yektu zapiti shos zrobiti public virtual void Send notifier Send riznomanitni dekoraciyi dodatkovi algoritmi nadsilannya spovishen class SmsNotifier NotifierDecoratorBase public SmsNotifier INotifier notifier base notifier public override void Send spochatku pracyuye vkladenij ob yekt potim obgortka base Send Console WriteLine Notify user with sms class EmailNotifier NotifierDecoratorBase public EmailNotifier INotifier notifier base notifier public override void Send base Send Console WriteLine Notify user with email class Program konfiguraciyi sistemi static bool isSmsNotificationEnabled true static bool isEmailNotificationEnabled false static void Main string args stvoryuyemo sposib opovishennya INotifier notifier new UserNotifier obgortayemo jogo zalezhno vid nalashtuvan sistemu if isSmsNotificationEnabled notifier new SmsNotifier notifier if isEmailNotificationEnabled notifier new EmailNotifier notifier vikonuyemo diyu notifier Send Console ReadLine Java Priklad realizaciyi movoyu Java import java util List import java util ArrayList import java util Collections abstract class WordSplitter abstract public List lt String gt getWords String wordString Prosto peretvoryuye ryadok sliv napisanih cherez probil na spisok sliv class PureWordSplitter extends WordSplitter public PureWordSplitter public List lt String gt getWords String wordString String wordArray wordString split List lt String gt wordList new ArrayList lt String gt wordArray length Collections addAll wordList wordArray return wordList Abstraktnij klas dlya dekoratoriv abstract class FilteringSplitter extends WordSplitter protected WordSplitter decoratedWordSplitter abstract public List lt String gt getWords String wordString Dekorator yakij vidalyaye slova z ne bilshe nizh dvoma bukvami class ShortWordsFilteringSplitter extends FilteringSplitter public ShortWordsFilteringSplitter WordSplitter wordSplitter decoratedWordSplitter wordSplitter public List lt String gt getWords String wordString List lt String gt wordList decoratedWordSplitter getWords wordString for int i 0 i lt wordList size if wordList get i length lt 2 wordList remove i else i return wordList Dekorator yakij vidalyaye zi spisku slova sho pochinayutsya z velikoyi bukvi class PropperNamesFilteringSplitter extends FilteringSplitter public PropperNamesFilteringSplitter WordSplitter wordSplitter decoratedWordSplitter wordSplitter public List lt String gt getWords String wordString List lt String gt wordList decoratedWordSplitter getWords wordString for int i 0 i lt wordList size String word wordList get i if word isEmpty Character isUpperCase word charAt 0 wordList remove i else i return wordList class Main public static void main String args WordSplitter wordSplitter new PropperNamesFilteringSplitter new ShortWordsFilteringSplitter new PureWordSplitter List lt String gt result wordSplitter getWords no hi Afrika yes ambitious come Ukraine Ua for String word result System out print word Zauvazhennya i komentariHocha ob yekt dekorator mozhe dodavati svoyu funkcionalnist do abo pislya funkcionalnosti osnovnogo ob yekta lancyuzhok stvoryuvanih ob yektiv zavzhdi povinen zakinchuvatisya ob yektom klasu ConcreteComponent Bazovi klasi movi Java shiroko vikoristovuyut shablon Dekorator dlya organizaciyi obrobki operacij vvedennya vivedennya PosilannyaDecorator design pattern 14 zhovtnya 2007 u Wayback Machine Design Patterns Elements of Reusable Object Oriented Software 9 listopada 2012 u Wayback Machine Dizajn paterni prosto yak dveri Chim vidriznyayetsya dekorator vid adaptera I pro fasad 22 grudnya 2014 u Wayback Machine Blog odnogo kiberaLiteraturaAlan Shallouej Dzhejms R Trott Shablony proektirovaniya Novyj podhod k obektno orientirovannomu analizu i proektirovaniyu Design Patterns Explained A New Perspective on Object Oriented Design M Vilyams 2002 288 s ISBN 0 201 71594 5