Эта статья поднимает множество проблем. Пожалуйста, помогите улучшить его или обсудите эти проблемы на странице обсуждения . ( Узнайте, как и когда удалить эти сообщения-шаблоны ) ( Узнайте, как и когда удалить этот шаблон сообщения )
|
В объектно-ориентированном программировании , то шаблон декоратора является шаблоном дизайна , который позволяет поведение , которые будет добавлено к отдельному объекту , динамически, не влияя на поведение других объектов из того же класса . [1] Шаблон декоратора часто бывает полезен для соблюдения принципа единой ответственности , поскольку он позволяет разделить функциональные возможности между классами с уникальными проблемами. [2] Использование декоратора может быть более эффективным, чем создание подклассов, потому что поведение объекта может быть расширено без определения полностью нового объекта.
Обзор [ править ]
Декоратор [3] шаблон проектирования является одной из двадцати трех хорошо известных шаблонов проектирования GoF ; в них описывается, как решать повторяющиеся проблемы проектирования и разрабатывать гибкое и многократно используемое объектно-ориентированное программное обеспечение, то есть объекты, которые легче реализовать, изменить, протестировать и повторно использовать.
Какие проблемы это может решить? [ редактировать ]
- Обязанности должны добавляться (и удаляться) к объекту динамически во время выполнения. [4]
- Должна быть предоставлена гибкая альтернатива подклассам для расширения функциональности.
При использовании подклассов разные подклассы расширяют класс по-разному. Но расширение привязано к классу во время компиляции и не может быть изменено во время выполнения. [ необходима цитата ]
Какое решение это описывает? [ редактировать ]
Определите Decorator
объекты, которые
- реализовать интерфейс расширенного (декорированного) объекта (
Component
) прозрачно, перенаправляя на него все запросы - выполнять дополнительные функции до / после пересылки запроса.
Это позволяет работать с различными Decorator
объектами для динамического расширения функциональных возможностей объекта во время выполнения.
См. Также схему классов и последовательности UML ниже.
Намерение [ править ]
Шаблон декоратора можно использовать для расширения (украшения) функциональности определенного объекта статически или, в некоторых случаях, во время выполнения , независимо от других экземпляров того же класса , при условии, что некоторая работа выполняется во время разработки. Это достигается путем разработки нового класса Decorator, который является оболочкой исходного класса. Этого оборачивания можно добиться, выполнив следующую последовательность шагов:
- Сделайте подкласс исходного класса Component в класс Decorator (см. Диаграмму UML);
- В классе Decorator добавьте указатель на компонент как поле;
- В декоратор классе, передать компонент в декоратор конструктор для инициализации компонента указателя;
- В декоратор классе вперед все компонентные методы к Компонента указателя; а также
- В классе ConcreteDecorator переопределите любые методы Component , поведение которых необходимо изменить.
Этот шаблон разработан так, что несколько декораторов могут быть наложены друг на друга, каждый раз добавляя новые функции к переопределенным методам.
Обратите внимание, что декораторы и исходный объект класса имеют общий набор функций. На предыдущей диаграмме метод operation () был доступен как в декорированной, так и в недекорированной версиях.
Возможности оформления (например, методы, свойства или другие члены) обычно определяются интерфейсом, примесью (также известной как признак ) или наследованием классов, которое совместно используется декораторами и декорируемым объектом. В предыдущем примере класс Component наследуется как ConcreteComponent, так и подклассами, которые наследуются от Decorator .
Шаблон декоратора - альтернатива подклассу . Создание подклассов добавляет поведение во время компиляции , и изменение затрагивает все экземпляры исходного класса; украшения могут обеспечить новое поведение в перспективе времени для выбранных объектов.
Это различие становится наиболее важным, когда есть несколько независимых способов расширения функциональности. В некоторых объектно-ориентированных языках программирования классы не могут быть созданы во время выполнения, и обычно во время разработки невозможно предсказать, какие комбинации расширений потребуются. Это означало бы, что нужно было бы создать новый класс для каждой возможной комбинации. Напротив, декораторы - это объекты, созданные во время выполнения, и их можно комбинировать для каждого использования. Реализации потоков ввода-вывода как Java, так и .NET Framework включают шаблон декоратора.
Мотивация [ править ]
В качестве примера рассмотрим окно в оконной системе . Чтобы разрешить прокрутку содержимого окна, можно добавить к нему горизонтальные или вертикальные полосы прокрутки , в зависимости от ситуации. Предположим, что окна представлены экземплярами интерфейса Window , и предположим, что этот класс не имеет функции для добавления полос прокрутки. Можно создать подкласс ScrollingWindow, который их предоставляет, или создать ScrollingWindowDecorator, который добавляет эту функциональность к существующим объектам Window . На этом этапе подойдет любое решение.
Теперь предположим, что вам также нужна возможность добавлять границы к окнам. Опять же, исходный класс Window не имеет поддержки. ScrollingWindow подкласс в настоящее время представляет собой проблему, поскольку она фактически создала новый вид окна. Если кто -то хочет , чтобы добавить поддержку границы для многих , но не все окна, нужно создавать подклассы WindowWithBorder и ScrollingWindowWithBorder и т.д. Эта проблема становится все хуже с добавляемого каждой новой функции или окна подтипа. Для решения декоратора создается новый BorderedWindowDecorator . Любая комбинация ScrollingWindowDecorator или BorderedWindowDecoratorможно украсить существующие окна. Если функциональность должна быть добавлена ко всем Windows, базовый класс можно изменить. С другой стороны, иногда (например, с использованием внешних фреймворков) изменение базового класса невозможно, законно или удобно.
В предыдущем примере классы SimpleWindow и WindowDecorator реализуют интерфейс Window , который определяет метод draw () и метод getDescription () , которые требуются в этом сценарии для украшения элемента управления окном.
Общие варианты использования [ править ]
Применение декораторов [ править ]
Добавление или удаление декораторов по команде (например, нажатие кнопки) является распространенным шаблоном пользовательского интерфейса, часто реализуемым вместе с шаблоном проектирования Command . Например, приложение для редактирования текста может иметь кнопку для выделения текста. При нажатии кнопки отдельные текстовые глифы, выбранные в данный момент, будут заключены в декораторы, которые изменяют их функцию draw (), заставляя их рисовать выделенным образом (реальная реализация, вероятно, также будет использовать систему разграничения для максимальной эффективности).
Еще один распространенный вариант использования - применение или удаление декораторов на основе изменений состояния. В зависимости от области действия декораторы могут применяться или удаляться массово. Точно так же шаблон проектирования State может быть реализован с использованием декораторов вместо объектов подклассов, инкапсулирующих изменяющуюся функциональность. Использование декораторов таким образом делает внутреннее состояние и функциональность объекта State более композиционным и способным обрабатывать произвольную сложность.
Использование в объектах-легковесах [ править ]
Украшение также часто используется в наилегчайшем шаблон дизайна. Объекты-легковесы делятся на два компонента: инвариантный компонент, который используется всеми объектами-легковесами; и вариант декорированного компонента, который может использоваться частично или полностью. Такое разбиение легковесного объекта предназначено для уменьшения потребления памяти. Декораторы обычно кэшируются и используются повторно. Все декораторы будут содержать общую ссылку на общий инвариантный объект. Если декорированное состояние является лишь частично вариантом, то декораторы также могут быть в некоторой степени общими, хотя следует проявлять осторожность, чтобы не изменять их состояние во время использования. См. Статью о схеме наилегчайшего веса.Больше подробностей. UITableView в iOS реализует легковесный шаблон таким образом - повторно используемые ячейки табличного представления являются декораторами, которые содержат ссылки на общий объект строки табличного представления, а ячейки кэшируются / повторно используются.
препятствия взаимодействия с декораторами [ править ]
Применение различных комбинаций декораторов к коллекции объектов создает некоторые проблемы, связанные с взаимодействием с коллекцией таким образом, чтобы в полной мере использовать функциональные возможности, добавленные декораторами. В таких случаях может быть полезно использование адаптера или посетителя . Взаимодействие с несколькими уровнями декораторов создает дополнительные проблемы, и логика адаптеров и посетителей должна быть разработана с учетом этого.
архитектурная значимость [ править ]
Декораторы поддерживают композиционный, а не иерархический подход к расширению функциональности сверху вниз. Декоратор позволяет добавлять или изменять поведение интерфейса во время выполнения. Их можно использовать для многослойного обертывания объектов произвольной комбинацией способов. То же самое с подклассами означает реализацию сложных сетей с множественным наследованием, которые неэффективны с точки зрения памяти и в определенный момент просто не могут масштабироваться. Точно так же попытка реализовать ту же функциональность с помощью свойств приводит к раздуванию каждого экземпляра объекта ненужными свойствами. По указанным выше причинам декораторы часто рассматриваются как эффективная с точки зрения памяти альтернатива подклассам.
Декораторы также могут использоваться для специализации объектов, которые не подклассифицируются, характеристики которых необходимо изменить во время выполнения (как упоминалось в другом месте), или, как правило, объектов, которым не хватает некоторых необходимых функций.
использование в улучшении API [ править ]
Шаблон декоратора также может дополнять узор фасада . Фасад предназначен для простого взаимодействия со сложной системой, которую он инкапсулирует, но не добавляет функциональности системе. Однако упаковка сложной системы предоставляет пространство, которое можно использовать для введения новых функций на основе координации подкомпонентов в системе. Например, паттерн фасада может объединять многоязычные словари в одном многоязычном интерфейсе словаря. Новый интерфейс может также предоставлять новые функции для перевода слов между языками. Это гибридный паттерн - единый интерфейс предоставляет пространство для дополнений. Думайте о декораторах как о не ограниченных оболочкой отдельных объектов, но также способных обертывать кластеры объектов в этом гибридном подходе.
Альтернативы декораторам [ править ]
В качестве альтернативы шаблону декоратора можно использовать адаптер , когда оболочка должна учитывать конкретный интерфейс и поддерживать полиморфное поведение, и фасад, когда требуется более простой или более простой интерфейс для базового объекта. [5]
Шаблон | Намерение |
---|---|
Адаптер | Преобразует один интерфейс в другой, чтобы он соответствовал ожиданиям клиента. |
Декоратор | Динамически добавляет ответственности к интерфейсу, упаковывая исходный код |
Фасад | Предоставляет упрощенный интерфейс |
Структура [ править ]
Схема классов и последовательности UML [ править ]
На приведенной выше диаграмме классов UML абстрактный класс поддерживает ссылку ( ) на декорированный объект ( ) и пересылает все запросы к нему ( ). Это делает прозрачным (невидимым) для клиентов .Decorator
component
Component
component.operation()
Decorator
Component
Подклассы ( Decorator1
, Decorator2
) реализуют дополнительное поведение ( addBehavior()
), которое должно быть добавлено в Component
(до / после пересылки ему запроса).
Диаграмма последовательности показывает взаимодействия во время выполнения: Client
объект работает, Decorator1
а Decorator2
объекты расширяют функциональность Component1
объекта.
В Client
звонки operation()
на Decorator1
, который перенаправляет запрос Decorator2
.Decorator2
выполняет addBehavior()
после перенаправления запроса Component1
и возвращается в Decorator1
, который выполняет addBehavior()
и возвращается в Client
.
Примеры [ править ]
Перейти [ править ]
пакет decologимпорт ( "журнал" "время" )// OperateFn представляет операции, требующие типа оформления OperateFn func ()// Украсить операция FUNC Decorate ( opFn OperateFn ) { Defer FUNC ( s время . Время ) { журнал . Printf ( "прошедшее время% 0,2d мс" , time . Since ( s ). Nanoseconds () / ( 1 << 20 )) } ( time . Now ())// функция реальной работы opFn () }// пакет main package mainimport ( "github.com/tkstorm/go-design/structural/decorator/decolog" "журнал" "математика / ранд" "время" )// вывод: // 19.08.2019 19:05:24 завершить действие a // 19.08.2019 19:05:24 прошедшее время 77 мс // 19.08.2019 19:05:24 завершить действие b // 2019/08/19 19:05:24 прошедшее время 88 мс func main () { // оформить журнал декологом . Decorate ( decolog . OperateFn ( DoActionA )) // украсить log b decolog . Украсить ( decolog . OperateFn ( DoActionB )) }func DoActionA () { время . Сон ( время . Продолжительность ( ранд . Intn ( 200 )) * время . Миллисекунды ) log . Println ( "завершить действие a" ) }func DoActionB () { время . Сон ( время . Продолжительность ( ранд . Intn ( 200 )) * время . Миллисекунды ) log . Println ( "завершить действие b" ) }
C ++ [ править ]
Здесь представлены два варианта: во-первых, динамический, составляемый во время выполнения декоратор (имеет проблемы с вызовом декорированных функций, если явно не проксируется) и декоратор, который использует наследование миксинов.
Динамический декоратор [ править ]
#include <iostream>#include <строка>struct Shape { виртуальная форма ~ Shape () = по умолчанию ; виртуальный std :: string GetName () const = 0 ; };struct Circle : Shape { void Resize ( float factor ) { radius * = factor ; } std :: string GetName () const override { return std :: string ( "Круг радиуса" ) + std :: to_string ( радиус ); } радиус поплавка = 10.0f ; };struct ColoredShape : Shape { ColoredShape ( const std :: string & color , Shape * shape ) : color ( цвет ), shape ( форма ) {} std :: string GetName () const override { return shape -> GetName () + "который окрашен" + color ; } std :: string color ; Форма * форма ; };int main () { Круг, круг ; ColoredShape coloured_shape ( "красный" , & круг ); std :: cout << color_shape . GetName () << std :: endl ;}
#include <память>#include <iostream>#include <строка>struct WebPage { виртуальный дисплей void () = 0 ; виртуальная ~ WebPage () = по умолчанию ; }; struct BasicWebPage : WebPage { std :: string html ; void display () override { std :: cout << "Базовая веб-страница" << std :: endl ; } ~ BasicWebPage () = по умолчанию ; };struct WebPageDecorator : WebPage { WebPageDecorator ( std :: unique_ptr < WebPage > webPage ) : _webPage ( std :: move ( webPage )) { } void display () override { _webPage -> display (); } ~ WebPageDecorator () = по умолчанию ; частный : std :: unique_ptr < WebPage> _webPage ; };struct AuthenticatedWebPage : WebPageDecorator { AuthenticatedWebPage ( std :: unique_ptr < WebPage > webPage ) : WebPageDecorator ( std :: move ( webPage )) {} void AuthenticateUser () { std :: cout << "аутентификация выполнена" << std :: endl ; } void display () переопределить { AuthenticateUser (); WebPageDecorator :: display (); } ~ AuthenticatedWebPage () = по умолчанию ; };struct AuthorizedWebPage : WebPageDecorator { AuthorizedWebPage ( std :: unique_ptr < WebPage > webPage ) : WebPageDecorator ( std :: move ( webPage )) {} void authorizedUser () { std :: cout << "авторизовано сделано" << std :: endl ; } void display () переопределить { authorizedUser (); WebPageDecorator :: display (); } ~ AuthorizedWebPage () = по умолчанию ; };Int основной ( INT ARGC , символ * ARGV []) { станд :: unique_ptr < WebPage > MyPage = станд :: make_unique < BasicWebPage > (); myPage = std :: make_unique < AuthorizedWebPage > ( std :: move ( myPage )); myPage = std :: make_unique < AuthenticatedWebPage > ( std :: move ( myPage )); myPage -> display (); std :: cout << std :: endl ; возврат 0 ; }
Статический декоратор (наследование миксинов) [ править ]
В этом примере демонстрируется реализация статического декоратора, которая возможна благодаря способности C ++ наследовать от аргумента шаблона.
#include <iostream>#include <строка>struct Circle { void Resize ( float factor ) { радиус * = фактор ; } std :: string GetName () const { return std :: string ( "Круг радиуса" ) + std :: to_string ( радиус ); } радиус поплавка = 10.0f ; };template < typename T > struct ColoredShape : public T { ColoredShape ( const std :: string & color ) : color ( color ) {} std :: string GetName () const { return T :: GetName () + "который окрашен" + цвет ; } std :: string color ; };int main () { ColoredShape < Круг > red_circle ( "красный" ); std :: cout << red_circle . GetName () << std :: endl ; красный_круг . Изменить размер ( 1.5f ); std :: cout << red_circle . GetName () << std :: endl ; }
Java [ править ]
Первый пример (сценарий окна / прокрутки) [ править ]
В следующем примере Java показано использование декораторов с использованием сценария окна / прокрутки.
// Класс интерфейса Window public interface Window { void draw (); // Рисует строку окна getDescription (); // Возвращает описание окна }// Реализация простого окна без полос прокрутки class SimpleWindow реализует Window { @Override public void draw () { // Рисование окна } @Override public String getDescription () { return "простое окно" ; } }
Следующие классы содержат декораторы для всех классов Window , включая сами классы декораторов.
// класс абстрактного декоратора - обратите внимание, что он реализует оконный абстрактный класс WindowDecorator реализует Window { private final Window windowToBeDecorated ; // оформляемое окно общедоступный WindowDecorator ( Window WindowToBeDecorated ) { это . windowToBeDecorated = windowToBeDecorated ; } @Override public void draw () { windowToBeDecorated . draw (); // Делегирование } @Override public String getDescription () { return windowToBeDecorated . getDescription (); // Делегирование } }// Первый конкретный декоратор, который добавляет функциональность вертикальной полосы прокрутки class VerticalScrollBarDecorator расширяет WindowDecorator { public VerticalScrollBarDecorator ( Window windowToBeDecorated ) { super ( windowToBeDecorated ); } @Override public void draw () { супер . draw (); drawVerticalScrollBar (); } private void drawVerticalScrollBar () { // Рисуем вертикальную полосу прокрутки } @Override public String getDescription () { return super . getDescription () + ", включая вертикальные полосы прокрутки" ; } }// Второй конкретный декоратор, который добавляет функциональность горизонтальной полосы прокрутки class HorizontalScrollBarDecorator расширяет WindowDecorator { public HorizontalScrollBarDecorator ( Window windowToBeDecorated ) { super ( windowToBeDecorated ); } @Override public void draw () { супер . draw (); drawHorizontalScrollBar (); } private void drawHorizontalScrollBar () { // Рисуем горизонтальную полосу прокрутки } @Override public String getDescription () { return super . getDescription () + ", включая горизонтальные полосы прокрутки" ; } }
Вот тестовая программа, которая создает полностью оформленный экземпляр Window (то есть с вертикальными и горизонтальными полосами прокрутки) и печатает его описание:
public class DecoratedWindowTest { public static void main ( String [] args ) { // Создаем оформленное окно с горизонтальной и вертикальной полосами прокрутки Window DecoratedWindow = new HorizontalScrollBarDecorator ( new VerticalScrollBarDecorator ( new SimpleWindow ())); // Распечатать описание окна System . из . Println ( decoratedWindow . getDescription ()); } }
Ниже представлен тестовый класс JUnit для разработки через тестирование.
import static org.junit.Assert.assertEquals ;import org.junit.Test ;общественный класс WindowDecoratorTest { @Test общественного аннулируются testWindowDecoratorTest () { Window decoratedWindow = новый HorizontalScrollBarDecorator ( новый VerticalScrollBarDecorator ( новый SimpleWindow ())); // утверждает , что описание действительно включает в себя горизонтальную + вертикальных полосах прокрутки assertEquals ( «простое окно, в том числе вертикальных полос прокрутки, в том числе горизонтальных полос прокрутки» , decoratedWindow . GetDescription ()) } }
Результатом этой программы является «простое окно, включая вертикальные полосы прокрутки, включая горизонтальные полосы прокрутки». Обратите внимание, как метод getDescription двух декораторов сначала извлекает описание декорированного Window и украшает его суффиксом.
Второй пример (сценарий приготовления кофе) [ править ]
Следующий пример Java иллюстрирует использование декораторов в сценарии приготовления кофе. В этом примере сценарий включает только стоимость и ингредиенты.
// Интерфейс Coffee определяет функциональность Coffee, реализованную декоратором public interface Coffee { public double getCost (); // Возвращает стоимость кофе public String getIngredients (); // Возвращает ингредиенты кофе }// Расширение простого кофе без дополнительных ингредиентов public class SimpleCoffee реализует Coffee { @Override public double getCost () { return 1 ; } @Override public String getIngredients () { return "Coffee" ; } }
Следующие классы содержат декораторы для всех классов Coffee , включая сами классы декораторов.
// Абстрактного класс декоратора - обратите внимание , что он реализует интерфейс кофе общественных абстрактного класса CoffeeDecorator орудия кофе { частного заключительного кофе decoratedCoffee ; public CoffeeDecorator ( Кофе c ) { this . DecorCoffee = c ; } @Override общественные двойной getCost () { методы // Реализация этого интерфейса обратного decoratedCoffee . getCost (); } @Override общественных строковых getIngredients () { возвращение decoratedCoffee . getIngredients (); } }// Декоратор WithMilk подмешивает молоко в кофе. // Обратите внимание, что он расширяет CoffeeDecorator. class WithMilk расширяет CoffeeDecorator { общедоступное WithMilk ( Кофе c ) { super ( c ); } @Override public double getCost () { // Переопределение методов, определенных в абстрактном суперклассе, return super . getCost () + 0,5 ; } @Override public String getIngredients () { return super . getIngredients () + ", молоко" ; } }// Декоратор WithSprinkles смешивает кофейные посыпки с кофе. // Обратите внимание, что он расширяет CoffeeDecorator. class WithSprinkles расширяет CoffeeDecorator { public WithSprinkles ( Coffee c ) { super ( c ); } @Override public double getCost () { return super . getCost () + 0,2 ; } @Override public String getIngredients () { return super . getIngredients () + ", окропляет" ; } }
Вот тестовая программа, которая создает экземпляр Coffee, который полностью украшен (с молоком и брызгами), рассчитывает стоимость кофе и распечатывает его ингредиенты:
открытый класс Main { public static void printInfo ( Coffee c ) { System . из . println ( "Стоимость:" + c . getCost () + "; Ингредиенты:" + c . getIngredients ()); } public static void main ( String [] args ) { Coffee c = new SimpleCoffee (); printInfo ( c ); c = новое WithMilk ( c ); printInfo ( c ); c = новый WithSprinkles ( c ); printInfo ( c ); } }
Результат этой программы представлен ниже:
Стоимость: 1.0; Состав: кофеСтоимость: 1.5; Состав: кофе, молокоСтоимость: 1,7; Состав: кофе, молоко, брызги.
PHP [ править ]
абстрактный класс Component { защищенные данные $ ; защищенное значение $ ; абстрактная публичная функция getData (); абстрактная публичная функция getValue (); }class ConcreteComponent расширяет Component { public function __construct () { $ this -> value = 1000 ; $ this -> data = "Конкретный компонент: \ t { $ this -> значение } \ n " ; } публичная функция getData () { return $ this -> data ; } публичная функция getValue () { return $ this -> value ; } }абстрактный класс Decorator расширяет Component { }class ConcreteDecorator1 расширяет Decorator { публичная функция __construct ( компонент $ data ) { $ this -> value = 500 ; $ this -> data = $ data ; } публичная функция getData () { return $ this -> data -> getData () . "Конкретный декоратор 1: \ t { $ this -> значение } \ n " ; } публичная функция getValue () { return $ this -> value + $ this -> data -> getValue (); } }class ConcreteDecorator2 расширяет Decorator { публичная функция __construct ( компонент $ data ) { $ this -> value = 500 ; $ this -> data = $ data ; } публичная функция getData () { return $ this -> data -> getData () . "Конкретный декоратор 2: \ t { $ this -> значение } \ n " ; } публичная функция getValue () { return $ this -> value + $ this -> data -> getValue (); } }класс Client { частный компонент $ ; публичная функция __construct () { $ this -> component = new ConcreteComponent (); $ this -> component = $ this -> wrapComponent ( $ this -> компонент ); echo $ this -> компонент -> getData (); echo "Клиент: \ t \ t \ t " ; echo $ this -> компонент -> getValue (); } частная функция wrapComponent ( Component $ component ) { $ component1 = new ConcreteDecorator1 ( $ component ); $ component2 = новый ConcreteDecorator2 ( $ component1 ); return $ component2 ; } }$ client = новый клиент ();// Результат: # quanton81// Компонент бетона: 1000 // Декоратор бетона 1: 500 // Декоратор бетона 2: 500 // Клиент: 2000
Python [ править ]
Следующий пример Python, взятый из Python Wiki - DecoratorPattern , показывает нам, как конвейерные декораторы для динамического добавления множества вариантов поведения в объект:
"" " Продемонстрированные декораторы в мире сетки 10x10 значений 0-255. " "" случайный импортdef s32_to_u16 ( x ): if x < 0 : sign = 0xF000 else : sign = 0 bottom = x & 0x00007FFF return bottom | знакdef seed_from_xy ( x , y ): return s32_to_u16 ( x ) | ( s32_to_u16 ( y ) << 16 )класс RandomSquare : def __init__ ( s , seed_modifier ): s . seed_modifier = seed_modifier def get ( s , x , y ): seed = seed_from_xy ( x , y ) ^ s . seed_modifier случайный . seed ( семя ) возвращается случайным образом . рандинт ( 0 , 255 )class DataSquare : def __init__ ( s , initial_value = None ): s . данные = [ начальное_значение ] * 10 * 10 def get ( s , x , y ): вернуть s . data [( y * 10 ) + x ] # да: это все 10x10 def set ( s , x , y , u ): s . данные [( y * 10 ) + x ] = uкласс CacheDecorator : def __init__ ( s , украшенный ): s . украшенный = украшенный s . cache = DataSquare () def get ( s , x , y ): если s . кеш . получить ( x , y ) == Нет : s . кеш . set ( x , y , s . оформлен . get ( x , y )) return s . кеш . получить ( х , у )Класс MaxDecorator : Защита __init__ ( s , украшенный , макс ): S . украшенный = украшенный s . макс = макс def get ( s , x , y ): если s . украшен . получить ( x , y ) > s . max : возврат s . максимальная отдача s . украшен . получить ( х , у )Класс MinDecorator : Защита __init__ ( s , украшенная , мин ): S . украшенный = украшенный s . мин = мин def get ( s , x , y ): если s . украшен . получить ( х , у ) < с . min : вернуть s . мин возврат с . украшен . получить ( х , у )класс VisibilityDecorator : def __init__ ( s , украшенный ): s . украшенный = украшенный def get ( s , x , y ): вернуть s . украшен . получить ( х , у ) def draw ( s ): for y in range ( 10 ): for x in range ( 10 ): print " % 3d " % s . получить ( x , y ), распечатать# Теперь создадим конвейер декораторов:random_square = RandomSquare ( 635 ) random_cache = CacheDecorator ( random_square ) max_filtered = MaxDecorator ( random_cache , 200 ) min_filtered = MinDecorator ( max_filtered , 100 ) final = VisibilityDecorator ( min_filtered )окончательный . draw ()
Примечание:
Пожалуйста, не путайте шаблон декоратора (или реализацию этого шаблона проектирования в Python - как в приведенном выше примере) с декораторами Python, функцией языка Python. Это разные вещи.
Второй после Python Wiki:
Шаблон декоратора - это шаблон, описанный в книге шаблонов дизайна. Это способ явно изменить поведение объекта, заключив его в украшающий объект с аналогичным интерфейсом. Это не следует путать с декораторами Python, которые представляют собой языковую функцию для динамического изменения функции или класса. [7]
Кристалл [ править ]
абстрактный класс кофе абстрактная деф стоимость абстрактная деф ингредиенты конец# Расширение простого кофе класса SimpleCoffee < кофе Защите стоимость 1 . 0 конец def ингредиенты "Кофе" конец конец# Аннотация декоратора класс CoffeeDecorator < Кофе защищен геттер decorated_coffee : Кофе def инициализировать ( @decorated_coffee ) конец def cost Decorated_coffee . конец стоимости def ингредиенты Decorated_coffee . ингредиенты конец конецкласс WithMilk < CoffeeDecorator Защиту стоимость супер + 0 . 5 конец def ингредиенты супер + ", молоко" конец конецкласс WithSprinkles < CoffeeDecorator Защиту стоимость супер + 0 . 2 конец def ингредиенты super + ", окропляет" конец конецкласс Программа четкости печати ( кофе : кофе ) ставит «Стоимость: {# кофе . стоимость } ; Состав: # { кофе . ингредиенты } » конец def инициализировать кофе = SimpleCoffee . новый принт ( кофе ) coffee = WithMilk . новый ( кофе ) принт ( кофе ) кофе = С брызгами . новый ( кофе ) печать ( кофе ) конец конецПрограмма . новый
Выход:
Стоимость: 1.0; Состав: кофеСтоимость: 1.5; Состав: кофе, молокоСтоимость: 1,7; Состав: кофе, молоко, брызги.
C # [ править ]
пространство имен WikiDesignPatterns { открытый интерфейс IBike { строка GetDetails (); двойной GetPrice (); } открытый класс AluminiumBike : IBike { public double GetPrice () { return 100 ; } публичная строка GetDetails () { return «Алюминиевый велосипед» ; } } открытый класс CarbonBike : IBike { общедоступный двойной GetPrice () { return 1000 ; } публичная строка GetDetails () { вернуть "Углерод" ; } } общественный абстрактные классы BikeAccessories : IBike { частные чтений IBike _bike ; общедоступные BikeAccessories ( велосипед IBike ) { _bike = bike ; } общедоступный виртуальный двойной GetPrice () { return _bike . GetPrice (); } общедоступная виртуальная строка GetDetails () { return _bike . GetDetails (); } } public class SecurityPackage : BikeAccessories { public SecurityPackage ( IBike bike ): base ( bike ) { } общедоступная строка переопределения GetDetails () { база возврата . GetDetails () + «+ Пакет безопасности» ; } public override double GetPrice () { база возврата . GetPrice () + 1 ; } } public class SportPackage : BikeAccessories { public SportPackage ( IBike bike ) : base ( bike ) { } общедоступная строка переопределения GetDetails () { база возврата . GetDetails () + «+ Спортивный пакет» ; } public override double GetPrice () { база возврата . GetPrice () + 10 ; } } открытый класс BikeShop { общедоступный статический недействительный UpgradeBike () { var basicBike = новый AluminiumBike (); Модернизированные аксессуары для велосипеда = новый SportPackage ( basicBike ); обновлено = новый пакет безопасности ( обновленный ); Консоль . WriteLine ( $ "Велосипед: '{обновлено.GetDetails ()}' Стоимость: {обновлено.GetPrice ()}" ); } } }
Выход:
Велосипед: «Алюминиевый велосипед + спортивный пакет + пакет безопасности» Стоимость: 111
См. Также [ править ]
- Составной узор
- Шаблон адаптера
- Абстрактный класс
- Абстрактная фабрика
- Аспектно-ориентированное программирование
- Неизменяемый объект
Ссылки [ править ]
- ^ Гамма, Эрих; и другие. (1995). Шаблоны проектирования . Ридинг, Массачусетс: Addison-Wesley Publishing Co, Inc., стр. 175 и далее . ISBN 0-201-63361-2.
- ^ «Как реализовать шаблон декоратора» . Архивировано из оригинала на 2015-07-07.
- ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Эддисон Уэсли. С. 175 и далее . ISBN 0-201-63361-2.CS1 maint: multiple names: authors list (link)
- ^ «Шаблон проектирования Decorator - проблема, решение и применимость» . w3sDesign.com . Проверено 12 августа 2017 .
- ^ Фриман, Эрик; Фриман, Элизабет; Сьерра, Кэти; Бейтс, Берт (2004). Хендриксон, Майк; Лукидес, Майк (ред.). Head First Design Patterns (мягкая обложка) . 1 . О'Рейли. стр. 243, 252, 258, 260. ISBN 978-0-596-00712-6. Проверено 2 июля 2012 .
- ^ «Шаблон проектирования Decorator - структура и взаимодействие» . w3sDesign.com . Проверено 12 августа 2017 .
- ^ "DecoratorPattern - Python Wiki" . wiki.python.org .
Внешние ссылки [ править ]
В Wikibook Computer Science Design Patterns есть страница по теме: Реализации декораторов на разных языках. |
- Реализация шаблона декоратора в Java
- Описание паттернов декоратора из Портлендского репозитория паттернов