Из Википедии, бесплатной энциклопедии
Перейти к навигации Перейти к поиску

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

Обзор [ править ]

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

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

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

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

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

Определите Decoratorобъекты, которые

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

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

Намерение [ править ]

Схема классов UML- декоратора

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

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

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

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

Возможности оформления (например, методы, свойства или другие члены) обычно определяются интерфейсом, примесью (также известной как признак ) или наследованием классов, которое совместно используется декораторами и декорируемым объектом. В предыдущем примере класс Component наследуется как ConcreteComponent, так и подклассами, которые наследуются от Decorator .

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

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

Мотивация [ править ]

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

В качестве примера рассмотрим окно в оконной системе . Чтобы разрешить прокрутку содержимого окна, можно добавить к нему горизонтальные или вертикальные полосы прокрутки , в зависимости от ситуации. Предположим, что окна представлены экземплярами интерфейса 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. [6]

На приведенной выше диаграмме классов UML абстрактный класс поддерживает ссылку ( ) на декорированный объект ( ) и пересылает все запросы к нему ( ). Это делает прозрачным (невидимым) для клиентов .DecoratorcomponentComponentcomponent.operation()DecoratorComponent

Подклассы ( 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

См. Также [ править ]

  • Составной узор
  • Шаблон адаптера
  • Абстрактный класс
  • Абстрактная фабрика
  • Аспектно-ориентированное программирование
  • Неизменяемый объект

Ссылки [ править ]

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

Внешние ссылки [ править ]

  • Реализация шаблона декоратора в Java
  • Описание паттернов декоратора из Портлендского репозитория паттернов