Читатели като вас помагат в подкрепа на MUO. Когато правите покупка чрез връзки на нашия сайт, ние може да спечелим комисионна за партньор. Прочетете още.

Един от най-важните принципи в разработката на софтуер е принципът на отворено-затворен дизайн. Този принцип на проектиране подчертава, че класовете трябва да бъдат отворени за разширяване, но затворени за модификация. Моделът на декоратора въплъщава принципа на отворено-затворен дизайн.

С дизайнерския модел на декоратора можете лесно да разширите клас, като му дадете ново поведение, без да променяте съществуващия му код. Моделът на декоратора прави това динамично по време на изпълнение, използвайки композиция. Този модел на проектиране е известен като гъвкава алтернатива на използването на наследяване за разширяване на поведението.

Как работи шаблонът за дизайн на декоратора?

Въпреки че моделът на декоратора е алтернатива на класово наследяване, той наистина включва някои аспекти на наследяването в своя дизайн. Ключов аспект на шаблона на декоратора е, че всички негови класове са свързани, пряко или косвено.

Типичният дизайн на декоратора има следната структура:

От диаграмата на класовете по-горе можете да видите, че шаблонът на декоратора има четири основни класа.

Компонент: това е абстрактен клас (или интерфейс), който служи като супертип за шаблона на декоратора.

Бетонен компонент: това са обектите, които можете да украсите с различни поведения по време на изпълнение. Те наследяват от компонентния интерфейс и изпълняват неговите абстрактни функции.

Декоратор: този клас е абстрактен и има същия супертип като обекта, който ще декорира. В диаграмата на класовете ще видите две връзки между класовете компонент и декоратор. Първата връзка е тази на наследяване; всеки декоратор е компонент. Второто отношение е на композиция; всеки декоратор има (или обгръща) компонент.

Декоратор на бетон: това са индивидуалните декоратори, които придават на компонент специфично поведение. Трябва да отбележите, че всеки конкретен декоратор има променлива на екземпляр, която съдържа препратка към компонент.

Внедряване на шаблона за дизайн на Decorator в Java

Примерно приложение за поръчка на пица може адекватно да демонстрира как да използвате модела на декоратора за разработване на приложения. Това примерно приложение за пица позволява на клиентите да поръчват пици с множество топинги. Първият клас на шаблона на декоратора е интерфейсът за пица:

публиченинтерфейспица{
публиченабстрактно низ описание();
публиченабстрактнодвойноцена();
}

Интерфейсът Pizza е класът на компонента. Така че можете да създадете един или повече конкретни класове от него. Компанията за производство на пици прави два основни вида пици, базирани на тяхното тесто. Един вид пица има тесто с мая:

публиченкласYeastCrustPizzaинструментипица{
@Override
публичен низ описание(){
връщане"Тесто за пица с мая";
}

@Override
публичендвойноцена(){
връщане18.00;
}
}

YeastCrustPizza е първият бетон Java клас на интерфейса Pizza. Другият наличен вид пица е плоската пица:

публиченкласFlatbreadCrustPizzaинструментипица{
@Override
публичен низ описание(){
връщане"Тесто за пица с плосък хляб";
}

@Override
публичендвойноцена(){
връщане15.00;
}
}

Класът FlatbreadCrustPizza е вторият конкретен компонент и, подобно на класа YeastCrustPizza, той имплементира всички абстрактни функции на интерфейса Pizza.

Декораторите

Класът декоратор винаги е абстрактен, така че не можете да създадете нов екземпляр директно от него. Но е необходимо да се установи връзка между различните декоратори и компонентите, които ще декорират.

публиченабстрактнокласToppingDecoratorинструментипица{
публичен низ описание(){
връщане„Неизвестен топинг“;
}
}

Класът ToppingDecorator представлява класа декоратор в това примерно приложение. Сега компанията за пица може да създава много различни гарнитури (или декоратори), използвайки класа ToppingDecorator. Да приемем, че една пицата може да има три различни вида топинги, а именно сирене, пеперони и гъби.

Топинг със сирене

публиченкласСиренесе простираToppingDecorator{
частен Пица пица;

публиченСирене(Пица пица){
това.pizza = пица;
}

@Override
публичен низ описание(){
връщане pizza.description() + ", топинг със сирене";
}

@Override
публичендвойноцена(){
връщанепица.цена() + 2.50;
}
}

Топинг от пеперони

публиченкласПеперонисе простираToppingDecorator{
частен Пица пица;

публиченПеперони(Пица пица){
това.pizza = пица;
}

@Override
публичен низ описание(){
връщане pizza.description() + ", топинг от пеперони";
}

@Override
публичендвойноцена(){
връщанепица.цена() + 3.50;
}
}

Топинг от гъби

публиченкласГъбасе простираToppingDecorator{
частен Пица пица;

публиченГъба(Пица пица){
това.pizza = пица;
}

@Override
публичен низ описание(){
връщане pizza.description() + ", топинг от гъби";
}

@Override
публичендвойноцена(){
връщанепица.цена() + 4.50;
}
}

Сега имате просто приложение, реализирано с помощта на дизайнерския модел на декоратора. Ако клиент поръча пица с мая със сирене и пеперони, тестовият код за този сценарий ще изглежда по следния начин:

публиченкласОсновен{
публиченстатиченневалиденосновен(Низ [] аргументи){
Пица pizza1 = нов YeastCrustPizza();
пица1 = нов Пеперони (пица1);
пица1 = нов Сирене (пица1);
System.out.println (pizza1.description() + " $" + pizza1.cost());
}
}

Изпълнението на този код ще доведе до следния изход в конзолата:

Както можете да видите, изходът посочва вида на пица заедно с общата й цена. Пицата започна като пица с дрожди за $18,00, но с шаблона за декориране приложението успя да добави нови функции и подходящата им цена към пицата. По този начин, давайки на пицата ново поведение, без да променя съществуващия код (пицата с дрожди).

С модела на декоратора можете също да приложите същото поведение към обект толкова пъти, колкото желаете. Ако клиент поръча пица с всичко в нея и малко допълнително сирене, можете да актуализирате основния клас със следния код, за да отразите това:

Пица pizza2 = нов YeastCrustPizza();
пица2 = нов Пеперони (пица2);
пица2 = нов Сирене (пица2);
пица2 = нов Сирене (пица2);
пица2 = нов Гъби (пица2);

System.out.println (pizza2.description() + " $" + pizza2.cost());

Актуализираното приложение ще изведе следния изход в конзолата:

Предимствата от използването на шаблона за дизайн на декоратора

Двете основни предимства на използването на дизайнерския модел на декоратора са сигурността и гъвкавостта. Моделът на декоратора ви позволява да разработите по-сигурен код, като не се намесвате в вече съществуващ защитен код. Вместо това разширява съществуващия код чрез композиция. Ефективно предотвратяване на въвеждането на нови грешки или нежелани странични ефекти.

Благодарение на композицията разработчикът също има голяма гъвкавост, когато използва модела на декоратора. Можете да внедрите нов декоратор по всяко време, за да добавите ново поведение, без да променяте съществуващия код и да прекъсвате приложението.