Шаблон декоратора - Decorator pattern

В объектно-ориентированного программирования, то декоратор шаблон это шаблон дизайна что позволяет добавлять поведение к личности объект, динамически, не влияя на поведение других объектов из того же класс.[1] Шаблон декоратора часто бывает полезен для соблюдения Принцип единой ответственности, поскольку он позволяет разделить функциональность между классами с уникальными проблемами.[2] Шаблон декоратора структурно почти идентичен схема цепочки ответственности с той разницей, что в цепочке ответственности запрос обрабатывает ровно один из классов, а в случае декоратора все классы обрабатывают запрос.

Обзор

В декоратор[3] шаблон дизайна - один из двадцати трех хорошо известных Шаблоны проектирования GoF; они описывают, как решать повторяющиеся проблемы проектирования и разрабатывать гибкое и многократно используемое объектно-ориентированное программное обеспечение, то есть объекты, которые легче реализовать, изменить, протестировать и повторно использовать.

Какие проблемы это может решить?

  • Обязанности должны добавляться (и удаляться) к объекту динамически во время выполнения.[4]
  • Должна быть предоставлена ​​гибкая альтернатива подклассам для расширения функциональности.

При использовании подклассов разные подклассы расширяют класс по-разному. Но расширение привязано к классу во время компиляции и не может быть изменено во время выполнения.

Какое решение это описывает?

Определить Декоратор объекты, которые

  • реализовать интерфейс расширенного (декорированного) объекта (Составная часть) прозрачно, перенаправляя на него все запросы
  • выполнять дополнительные функции до / после пересылки запроса.

Это позволяет работать с разными Декоратор объекты для динамического расширения функциональности объекта во время выполнения.
См. Также схему классов и последовательности UML ниже.

Намерение

Декоратор UML диаграмма классов

Шаблон декоратора можно использовать для расширения (украшения) функциональности определенного объекта статически или, в некоторых случаях, в время выполнения, независимо от других экземпляров того же класс при условии, что во время разработки будет проведена определенная работа. Это достигается за счет разработки нового Декоратор класс, который обертывания оригинальный класс. Такое обертывание может быть достигнуто следующей последовательностью шагов:

  1. Подкласс оригинала Составная часть класс в Декоратор класс (см. диаграмму UML);
  2. в Декоратор класс, добавьте Составная часть указатель как поле;
  3. в Декоратор класс, пройти Составная часть к Декоратор конструктор для инициализации Составная часть указатель;
  4. в Декоратор класс, вперед все Составная часть методы для Составная часть указатель; и
  5. В классе ConcreteDecorator переопределите любые Составная часть метод (ы), поведение которых необходимо изменить.

Этот шаблон разработан таким образом, что несколько декораторов могут быть наложены друг на друга, каждый раз добавляя новые функции к переопределенным методам.

Обратите внимание, что декораторы и исходный объект класса имеют общий набор функций. На предыдущей диаграмме метод operation () был доступен как в декорированной, так и в недекорированной версиях.

Функции оформления (например, методы, свойства или другие элементы) обычно определяются интерфейсом, миксин (a.k.a. черта характера ) или наследование класса, которое разделяют декораторы и декорируемый объект. В предыдущем примере класс Составная часть наследуется как ConcreteComponent, так и подклассами, которые происходят от Декоратор.

Шаблон декоратора является альтернативой подклассы. Создание подкласса добавляет поведение в время компиляции, и изменение затрагивает все экземпляры исходного класса; украшение может обеспечить новое поведение в время выполнения для выбранных объектов.

Это различие становится наиболее важным при наличии нескольких независимый способы расширения функциональности. В некоторых объектно-ориентированные языки программирования, классы не могут быть созданы во время выполнения, и, как правило, во время разработки невозможно предсказать, какие комбинации расширений потребуются. Это означало бы, что нужно было бы создать новый класс для каждой возможной комбинации. Напротив, декораторы - это объекты, созданные во время выполнения, и их можно комбинировать для каждого использования. Реализации I / O Streams обоих Ява и .NET Framework включить шаблон декоратора.

Мотивация

Схема UML для примера окна

В качестве примера рассмотрим окно в оконная система. Позволять прокрутка содержимого окна можно добавить горизонтальный или вертикальный полосы прокрутки к нему, в зависимости от обстоятельств. Предположим, окна представлены экземплярами Окно интерфейс и предположим, что этот класс не имеет функции для добавления полос прокрутки. Можно создать подкласс ScrollingWindow который их предоставляет, или создать ScrollingWindowDecorator который добавляет эту функциональность к существующим Окно объекты. На данный момент подойдет любое решение.

Теперь предположим, что кому-то также нужна возможность добавлять границы к окнам. Опять же оригинал Окно класс не имеет поддержки. В ScrollingWindow подкласс теперь представляет проблему, потому что он фактически создал новый вид окна. Если кто-то хочет добавить поддержку границ многим, но не все windows, необходимо создать подклассы WindowWithBorder и ScrollingWindowWithBorderи т. д. Эта проблема усугубляется при добавлении каждой новой функции или подтипа окна. Для декораторного решения новый BorderedWindowDecorator создано. Любая комбинация ScrollingWindowDecorator или BorderedWindowDecorator можно украсить существующие окна. Если функциональность должна быть добавлена ​​ко всем Windows, базовый класс можно изменить. С другой стороны, иногда (например, с использованием внешних фреймворков) изменение базового класса невозможно, законно или удобно.

В предыдущем примере SimpleWindow и WindowDecorator классы реализуют Окно интерфейс, который определяет рисовать() метод и getDescription () , которые требуются в этом сценарии, чтобы украсить оконный элемент управления.

Применение

Декоратор позволяет добавлять или изменять поведение интерфейса во время выполнения. В качестве альтернативы адаптер может использоваться, когда оболочка должна учитывать конкретный интерфейс и поддерживать полиморфный поведение, и Фасад когда желателен более простой или простой интерфейс для базового объекта.[5]

ШаблонНамерение
АдаптерПреобразует один интерфейс в другой, чтобы он соответствовал ожиданиям клиента.
ДекораторДинамически добавляет ответственности к интерфейсу, упаковывая исходный код
ФасадПредоставляет упрощенный интерфейс

Структура

Схема классов и последовательности UML

Пример класса UML и диаграммы последовательности для шаблона проектирования Decorator. [6]

В приведенном выше UML диаграмма классов, аннотация Декоратор класс поддерживает ссылку (составная часть) к украшаемому объекту (Составная часть) и пересылает ему все запросы (component.operation ()). Это делает Декоратор прозрачный (невидимый) для клиентов Составная часть.

Подклассы (Decorator1,Декоратор2) реализовать дополнительное поведение (addBehavior ()), который следует добавить в Составная часть (до / после отправки ему запроса).
Диаграмма последовательности показывает взаимодействия во время выполнения: Клиент объект работает через Decorator1 и Декоратор2 объекты для расширения функциональности Компонент1 объект.
В Клиент звонки операция () на Decorator1, который перенаправляет запрос на Декоратор2.Декоратор2 выполняет addBehavior () после пересылки запроса на Компонент1 и возвращается вDecorator1, который выполняет addBehavior () и возвращается в Клиент.

Примеры

Идти

пакет декологимпорт (	"бревно"	"время")// OperateFn представляет операции, требующие оформлениятип OperateFn func()// Украшаем операциюfunc Украсить(opFn OperateFn)  {	отложить func(s время.Время) {		журнал.Printf("прошедшее время% 0,2d мс", время.поскольку(s).Наносекунды()/(1<<20))	}(время.Сейчас же())	// функция реальной работы	opFn()}// основной пакетпакет основнойимпорт (	"github.com/tkstorm/go-design/structural/decorator/decolog"	"бревно"	"математика / ранд"	"время")//выход:// 19.08.2019 19:05:24 финиш акция а// 2019/08/19 19:05:24 затраченное время 77 мс// 19.08.2019 19:05:24 финиш заезд b// 2019/08/19 19:05:24 затраченное время 88 мсfunc основной() {	// украшаем журнал	деколог.Украсить(деколог.OperateFn(DoActionA))	// украшаем бревно b	деколог.Украсить(деколог.OperateFn(DoActionB))}func DoActionA() {	время.Спать(время.Продолжительность(ранд.Intn(200)) * время.Миллисекунды)	журнал.Println("закончить действие")}func DoActionB() {	время.Спать(время.Продолжительность(ранд.Intn(200)) * время.Миллисекунды)	журнал.Println("закончить действие б")}

C ++

Здесь представлены два варианта: во-первых, динамический, составляемый во время выполнения декоратор (имеет проблемы с вызовом украшенных функций, если явно не проксируется) и декоратор, который использует наследование миксинов.

Динамический декоратор

#включают <iostream>#включают <string>структура Форма {  виртуальный ~Форма() = по умолчанию;  виртуальный стандартное::строка GetName() const = 0;};структура Круг : Форма {  пустота Изменить размер(плавать фактор) { радиус *= фактор; }  стандартное::строка GetName() const отменять {    вернуть стандартное::строка(«Круг радиуса») + стандартное::нанизывать(радиус);  }  плавать радиус = 10.0f;};структура Цветной : Форма {  Цветной(const стандартное::строка& цвет, Форма* форма)      : цвет(цвет), форма(форма) {}  стандартное::строка GetName() const отменять {    вернуть форма->GetName() + "который окрашен" + цвет;  }  стандартное::строка цвет;  Форма* форма;};int основной() {  Круг круг;  Цветной цветная_ форма("красный", &круг);  стандартное::cout << цветная_ форма.GetName() << стандартное::конец;}
#включают <memory>#включают <iostream>#включают <string>структура Страница в Интернете{    виртуальный пустота отображать()=0;    виртуальный ~Страница в Интернете() = по умолчанию;};структура BasicWebPage : Страница в Интернете{    стандартное::строка html;    пустота отображать() отменять    {        стандартное::cout << «Базовая ВЕБ-страница» << стандартное::конец;    }    ~BasicWebPage()=по умолчанию;};структура WebPageDecorator : Страница в Интернете{    WebPageDecorator(стандартное::unique_ptr<Страница в Интернете> страница в Интернете): _страница в Интернете(стандартное::шаг(страница в Интернете))    {    }    пустота отображать() отменять    {        _страница в Интернете->отображать();    }    ~WebPageDecorator()=по умолчанию;частный:    стандартное::unique_ptr<Страница в Интернете> _страница в Интернете;};структура AuthenticatedWebPage : WebPageDecorator{    AuthenticatedWebPage(стандартное::unique_ptr<Страница в Интернете> страница в Интернете):     WebPageDecorator(стандартное::шаг(страница в Интернете))    {}    пустота AuthenticateUser()    {        стандартное::cout << "аутентификация выполнена" << стандартное::конец;    }    пустота отображать() отменять    {        AuthenticateUser();        WebPageDecorator::отображать();    }    ~AuthenticatedWebPage()=по умолчанию;};структура AuthorizedWebPage : WebPageDecorator{    AuthorizedWebPage(стандартное::unique_ptr<Страница в Интернете> страница в Интернете):     WebPageDecorator(стандартное::шаг(страница в Интернете))    {}    пустота авторизованный пользователь()    {        стандартное::cout << "авторизовано сделано" << стандартное::конец;    }    пустота отображать() отменять    {        авторизованный пользователь();        WebPageDecorator::отображать();    }    ~AuthorizedWebPage()=по умолчанию;};int основной(int argc, char* argv[]){    стандартное::unique_ptr<Страница в Интернете> моя страница = стандартное::make_unique<BasicWebPage>();    моя страница = стандартное::make_unique<AuthorizedWebPage>(стандартное::шаг(моя страница));    моя страница = стандартное::make_unique<AuthenticatedWebPage>(стандартное::шаг(моя страница));    моя страница->отображать();    стандартное::cout << стандартное::конец;    вернуть 0;}

Статический декоратор (наследование миксинов)

В этом примере демонстрируется реализация статического декоратора, которая возможна благодаря способности C ++ наследовать от аргумента шаблона.

#включают <iostream>#включают <string>структура Круг {  пустота Изменить размер(плавать фактор) { радиус *= фактор; }  стандартное::строка GetName() const {    вернуть стандартное::строка(«Круг радиуса») + стандартное::нанизывать(радиус);  }  плавать радиус = 10.0f;};шаблон <typename Т>структура Цветной : общественный Т {  Цветной(const стандартное::строка& цвет) : цвет(цвет) {}  стандартное::строка GetName() const {    вернуть Т::GetName() + "который окрашен" + цвет;  }  стандартное::строка цвет;};int основной() {  Цветной<Круг> красный_круг("красный");  стандартное::cout << красный_круг.GetName() << стандартное::конец;  красный_круг.Изменить размер(1.5f);  стандартное::cout << красный_круг.GetName() << стандартное::конец;}

Ява

Первый пример (сценарий окна / прокрутки)

В следующем примере Java показано использование декораторов в сценарии окна / прокрутки.

// Класс интерфейса Windowобщественный интерфейс Окно {    пустота привлечь(); // Рисует окно    Строка getDescription(); // Возвращает описание окна}// Реализация простого окна без полос прокруткикласс SimpleWindow орудия Окно {    @Override    общественный пустота привлечь() {        // Рисуем окно    }    @Override    общественный Строка getDescription() {        вернуть "простое окно";    }}

Следующие классы содержат декораторы для всех Окно классы, включая сами классы декораторов.

// абстрактный класс декоратора - обратите внимание, что он реализует WindowАбстрактные класс WindowDecorator орудия Окно {    частный окончательный Окно windowToBeDecorated; // оформляемое окно    общественный WindowDecorator (Окно windowToBeDecorated) {        этот.windowToBeDecorated = windowToBeDecorated;    }    @Override    общественный пустота привлечь() {        windowToBeDecorated.привлечь(); //Делегация    }    @Override    общественный Строка getDescription() {        вернуть windowToBeDecorated.getDescription(); //Делегация    }}// Первый конкретный декоратор, который добавляет функциональность вертикальной полосы прокруткикласс VerticalScrollBarDecorator расширяет WindowDecorator {    общественный Вертикальная полоса прокруткиДекоратор (Окно windowToBeDecorated) {        супер(windowToBeDecorated);    }    @Override    общественный пустота привлечь() {        супер.привлечь();        drawVerticalScrollBar();    }    частный пустота drawVerticalScrollBar() {        // Рисуем вертикальную полосу прокрутки    }    @Override    общественный Строка getDescription() {        вернуть супер.getDescription() + ", включая вертикальные полосы прокрутки";    }}// Второй конкретный декоратор, который добавляет функциональность горизонтальной полосы прокруткикласс HorizontalScrollBarDecorator расширяет WindowDecorator {    общественный HorizontalScrollBarDecorator (Окно windowToBeDecorated) {        супер(windowToBeDecorated);    }    @Override    общественный пустота привлечь() {        супер.привлечь();        drawHorizontalScrollBar();    }    частный пустота drawHorizontalScrollBar() {        // Рисуем горизонтальную полосу прокрутки    }    @Override    общественный Строка getDescription() {        вернуть супер.getDescription() + ", включая горизонтальные полосы прокрутки";    }}

Вот тестовая программа, которая создает Окно экземпляр, который полностью оформлен (то есть с вертикальной и горизонтальной полосами прокрутки), и печатает его описание:

общественный класс DecoratedWindowTest {    общественный статический пустота основной(Строка[] аргументы) {        // Создаем оформленное окно с горизонтальной и вертикальной полосами прокрутки        Окно украшеноОкно = новый HorizontalScrollBarDecorator (                новый VerticalScrollBarDecorator (новый SimpleWindow()));        // Распечатываем описание окна        Система.вне.println(украшеноОкно.getDescription());    }}

Ниже представлен тестовый класс JUnit для разработки через тестирование.

импорт статических org.junit.Assert.assertEquals;импорт org.junit.Test;общественный класс WindowDecoratorTest {	@Тест	общественный пустота testWindowDecoratorTest() {	    Окно украшеноОкно = новый HorizontalScrollBarDecorator(новый VerticalScrollBarDecorator(новый SimpleWindow()));      	    // утверждаем, что описание действительно включает горизонтальные + вертикальные полосы прокрутки            assertEquals("простое окно, включая вертикальные полосы прокрутки, включая горизонтальные полосы прокрутки", украшеноОкно.getDescription())	}}

Результатом этой программы является «простое окно, включая вертикальные полосы прокрутки, включая горизонтальные полосы прокрутки». Обратите внимание, как getDescription метод двух декораторов сначала получает декорированные Окноописание и украшает это с суффиксом.

Второй пример (сценарий приготовления кофе)

Следующий пример Java иллюстрирует использование декораторов в сценарии приготовления кофе. В этом примере сценарий включает только стоимость и ингредиенты.

// Интерфейс Coffee определяет функциональность Coffee, реализованную декораторомобщественный интерфейс Кофе {    общественный двойной getCost(); // Возвращает стоимость кофе    общественный Строка getIngredients(); // Возвращает ингредиенты кофе}// Расширение простого кофе без дополнительных ингредиентовобщественный класс SimpleCoffee орудия Кофе {    @Override    общественный двойной getCost() {        вернуть 1;    }    @Override    общественный Строка getIngredients() {        вернуть "Кофе";    }}

Следующие классы содержат декораторы для всех Кофе классы, включая сами классы декораторов.

// Класс абстрактного декоратора - обратите внимание, что он реализует интерфейс Coffeeобщественный Абстрактные класс КофеДекоратор орудия Кофе {    частный окончательный Кофе украшенныйКофе;    общественный КофеДекоратор(Кофе c) {        этот.украшенныйКофе = c;    }    @Override    общественный двойной getCost() { // Реализация методов интерфейса        вернуть украшенныйКофе.getCost();    }    @Override    общественный Строка getIngredients() {        вернуть украшенныйКофе.getIngredients();    }}// Декоратор WithMilk подмешивает молоко в кофе.// Обратите внимание, что он расширяет CoffeeDecorator.класс С молоком расширяет КофеДекоратор {    общественный С молоком(Кофе c) {        супер(c);    }    @Override    общественный двойной getCost() { // Переопределение методов, определенных в абстрактном суперклассе        вернуть супер.getCost() + 0.5;    }    @Override    общественный Строка getIngredients() {        вернуть супер.getIngredients() + ", Молоко";    }}// Декоратор WithSprinkles смешивает кофейные посыпки.// Обратите внимание, что он расширяет CoffeeDecorator.класс С брызгами расширяет КофеДекоратор {    общественный С брызгами(Кофе c) {        супер(c);    }    @Override    общественный двойной getCost() {        вернуть супер.getCost() + 0.2;    }    @Override    общественный Строка getIngredients() {        вернуть супер.getIngredients() + ", Окропляет";    }}

Вот тестовая программа, которая создает Кофе экземпляр, который полностью декорирован (с молоком и брызгами), рассчитывает стоимость кофе и распечатывает его ингредиенты:

общественный класс Основной {    общественный статический пустота printInfo(Кофе c) {        Система.вне.println("Расходы: " + c.getCost() + "; Ингредиенты:" + c.getIngredients());    }    общественный статический пустота основной(Строка[] аргументы) {        Кофе c = новый SimpleCoffee();        printInfo(c);        c = новый С молоком(c);        printInfo(c);        c = новый С брызгами(c);        printInfo(c);    }}

Результат этой программы представлен ниже:

Стоимость: 1.0; Состав: Кофе Стоимость: 1,5; Состав: кофе, молоко Стоимость: 1,7; Ингредиенты: кофе, молоко, брызги

PHP

Абстрактные класс Составная часть{    защищенный $ данные;    защищенный $ значение;    Абстрактные общественный функция получить данные();    Абстрактные общественный функция getValue();}класс БетонКомпонент расширяет Составная часть{    общественный функция __construct()    {        $ это->ценность = 1000;        $ это->данные = «Бетонный компонент: т{$ это->ценность} п";    }    общественный функция получить данные()    {        вернуть $ это->данные;    }    общественный функция getValue()    {        вернуть $ это->ценность;    }}Абстрактные класс Декоратор расширяет Составная часть{    }класс БетонДекоратор1 расширяет Декоратор{    общественный функция __construct(Составная часть $ данные)    {        $ это->ценность = 500;        $ это->данные = $ данные;    }    общественный функция получить данные()    {        вернуть $ это->данные->получить данные() . «Бетонный декоратор 1: т{$ это->ценность} п";    }    общественный функция getValue()    {        вернуть $ это->ценность + $ это->данные->getValue();    }}класс БетонДекоратор2 расширяет Декоратор{    общественный функция __construct(Составная часть $ данные)    {        $ это->ценность = 500;        $ это->данные = $ данные;    }    общественный функция получить данные()    {        вернуть $ это->данные->получить данные() . «Бетонный декоратор 2: т{$ это->ценность} п";    }    общественный функция getValue()    {        вернуть $ это->ценность + $ это->данные->getValue();    }}класс Клиент{    частный $ компонент;    общественный функция __construct()    {        $ это->составная часть = новый БетонКомпонент();        $ это->составная часть = $ это->wrapComponent($ это->составная часть);        эхо $ это->составная часть->получить данные();        эхо «Клиент: т  т  т";        эхо $ это->составная часть->getValue();    }    частный функция wrapComponent(Составная часть $ component)    {        $ component1 = новый БетонДекоратор1($ component);        $ component2 = новый БетонДекоратор2($ component1);        вернуть $ component2;    }}$ клиент = новый Клиент();// Результат: # quanton81// Бетонный компонент: 1000// Бетонный декоратор 1: 500// Бетонный декоратор 2: 500// Клиент: 2000

Python

Следующий пример Python, взятый из Python Wiki - DecoratorPattern, показывает нам, как конвейерные декораторы для динамического добавления множества вариантов поведения в объект:

"""Продемонстрированные декораторы в мире сетки 10х10 значений 0-255. """импорт случайныйdef s32_to_u16(Икс):    если Икс < 0:        знак = 0xF000    еще:        знак = 0    дно = Икс & 0x00007FFF    вернуть дно | знакdef seed_from_xy(Икс, у):    вернуть s32_to_u16(Икс) | (s32_to_u16(у) << 16)класс RandomSquare:    def __в этом__(s, seed_modifier):        s.seed_modifier = seed_modifier    def получать(s, Икс, у):        семя = seed_from_xy(Икс, у) ^ s.seed_modifier        случайный.семя(семя)        вернуть случайный.Randint(0, 255)класс DataSquare:    def __в этом__(s, Первоначальный значение=Никто):        s.данные = [Первоначальный значение] * 10 * 10    def получать(s, Икс, у):        вернуть s.данные[(у * 10) + Икс]  # да: это все 10х10    def набор(s, Икс, у, ты):        s.данные[(у * 10) + Икс] = тыкласс CacheDecorator:    def __в этом__(s, украшенный):        s.украшенный = украшенный        s.тайник = DataSquare()    def получать(s, Икс, у):        если s.тайник.получать(Икс, у) == Никто:            s.тайник.набор(Икс, у, s.украшенный.получать(Икс, у))        вернуть s.тайник.получать(Икс, у)класс MaxDecorator:    def __в этом__(s, украшенный, Максимум):        s.украшенный = украшенный        s.Максимум = Максимум    def получать(s, Икс, у):        если s.украшенный.получать(Икс, у) > s.Максимум:            вернуть s.Максимум        вернуть s.украшенный.получать(Икс, у)класс MinDecorator:    def __в этом__(s, украшенный, мин):        s.украшенный = украшенный        s.мин = мин    def получать(s, Икс, у):        если s.украшенный.получать(Икс, у) < s.мин:            вернуть s.мин        вернуть s.украшенный.получать(Икс, у)класс ВидимостьДекоратор:    def __в этом__(s, украшенный):        s.украшенный = украшенный    def получать(s, Икс, у):        вернуть s.украшенный.получать(Икс, у)    def привлечь(s):        для у в ассортимент(10):            для Икс в ассортимент(10):                Распечатать "% 3d" % s.получать(Икс, у),            Распечатать# Теперь создайте конвейер декораторов:random_square = RandomSquare(635)random_cache = CacheDecorator(random_square)max_filtered = MaxDecorator(random_cache, 200)min_filtered = MinDecorator(max_filtered, 100)окончательный = ВидимостьДекоратор(min_filtered)окончательный.привлечь()

Заметка:

Пожалуйста, не путайте шаблон декоратора (или реализацию этого шаблона проектирования в Python - как в примере выше) с Декораторы Python, функция языка Python. Это разные вещи.

Второй после Python Wiki:

Шаблон декоратора - это шаблон, описанный в книге шаблонов дизайна. Это способ явно изменить поведение объекта, заключив его в украшающий объект с аналогичным интерфейсом. Его не следует путать с декораторами Python, которые представляют собой языковую особенность для динамического изменения функции или класса.[7]

Кристалл

Абстрактные класс Кофе  Абстрактные def Стоимость  Абстрактные def ингредиентыконец# Продление простого кофекласс SimpleCoffee < Кофе  def Стоимость    1.0  конец  def ингредиенты    "Кофе"  конецконец# Абстрактный декораторкласс КофеДекоратор < Кофе  защищенный добытчик Decorated_coffee : Кофе  def инициализировать(@decorated_coffee)  конец  def Стоимость    Decorated_coffee.Стоимость  конец  def ингредиенты    Decorated_coffee.ингредиенты  конецконецкласс С молоком < КофеДекоратор  def Стоимость    супер + 0.5  конец  def ингредиенты    супер + ", Молоко"  конецконецкласс С брызгами < КофеДекоратор  def Стоимость    супер + 0.2  конец  def ингредиенты    супер + ", Окропляет"  конецконецкласс Программа  def Распечатать(кофе : Кофе)    ставит "Расходы: #{кофе.Стоимость}; Ингредиенты: #{кофе.ингредиенты}"  конец  def инициализировать    кофе = SimpleCoffee.новый    Распечатать(кофе)    кофе = С молоком.новый(кофе)    Распечатать(кофе)    кофе = С брызгами.новый(кофе)    Распечатать(кофе)  конецконецПрограмма.новый

Вывод:

Стоимость: 1.0; Состав: Кофе Стоимость: 1,5; Состав: кофе, молоко Стоимость: 1,7; Ингредиенты: кофе, молоко, брызги

C #

пространство имен WikiDesignPatterns{    общественный интерфейс IBike    {        строка GetDetails();        двойной GetPrice();    }    общественный класс АлюминийВелосипед : IBike    {        общественный двойной GetPrice()        {            вернуть 100;        }        общественный строка GetDetails()        {            вернуть «Алюминиевый велосипед»;        }    }    общественный класс КарбонБайк : IBike    {        общественный двойной GetPrice()        {            вернуть 1000;        }        общественный строка GetDetails()        {            вернуть «Карбон»;        }    }    общественный Абстрактные класс Велосипед : IBike    {        частный только чтение IBike _велосипед;        общественный Велосипед(IBike велосипед)        {            _велосипед = велосипед;        }        общественный виртуальный двойной GetPrice()        {            вернуть _велосипед.GetPrice();        }        общественный виртуальный строка GetDetails()        {            вернуть _велосипед.GetDetails();        }    }    общественный класс Пакет безопасности : Велосипед    {        общественный Пакет безопасности(IBike велосипед):база(велосипед)        {        }        общественный отменять строка GetDetails()        {            вернуть база.GetDetails() + «+ Пакет безопасности»;        }        общественный отменять двойной GetPrice()        {            вернуть база.GetPrice() + 1;        }    }    общественный класс СпортПакет : Велосипед    {        общественный СпортПакет(IBike велосипед) : база(велосипед)        {        }        общественный отменять строка GetDetails()        {            вернуть база.GetDetails() + «+ Спорт пакет»;        }        общественный отменять двойной GetPrice()        {            вернуть база.GetPrice() + 10;        }    }    общественный класс Магазин велосипедов    {        общественный статический пустота UpgradeBike()        {            вар basicBike = новый АлюминийВелосипед();            Велосипед улучшенный = новый СпортПакет(basicBike);            улучшенный = новый Пакет безопасности(улучшенный);            Консоль.WriteLine($"Велосипед: '{обновлено.GetDetails ()}' Стоимость: {обновлено.GetPrice ()}");        }    }}

Вывод:

Велосипед: «Алюминиевый велосипед + спортивный пакет + пакет безопасности» Стоимость: 111

Смотрите также

использованная литература

  1. ^ Гамма, Эрих; и другие. (1995). Шаблоны проектирования. Ридинг, Массачусетс: Addison-Wesley Publishing Co, Inc., стр.175ff. ISBN  0-201-63361-2.
  2. ^ «Как реализовать узор декоратора». Архивировано из оригинал на 2015-07-07.
  3. ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Паттерны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования. Эддисон Уэсли. стр.175ff. ISBN  0-201-63361-2.CS1 maint: несколько имен: список авторов (ссылка на сайт)
  4. ^ «Шаблон проектирования Decorator - проблема, решение и применимость». w3sDesign.com. Получено 2017-08-12.
  5. ^ Фриман, Эрик; Фриман, Элизабет; Сьерра, Кэти; Бейтс, Берт (2004). Хендриксон, Майк; Лукидес, Майк (ред.). Шаблоны проектирования Head First (мягкая обложка). 1. О'Рейли. С. 243, 252, 258, 260. ISBN  978-0-596-00712-6. Получено 2012-07-02.
  6. ^ «Шаблон проектирования Decorator - структура и взаимодействие». w3sDesign.com. Получено 2017-08-12.
  7. ^ "DecoratorPattern - Python Wiki". wiki.python.org.

внешние ссылки