Шаблон декоратора
В объектно-ориентированном программировании — шаблон декоратора это шаблон проектирования , который позволяет динамически добавлять поведение к отдельному объекту , не затрагивая поведение других экземпляров того же класса . [1] Шаблон декоратора часто полезен для соблюдения принципа единой ответственности , поскольку он позволяет разделить функциональность между классами с уникальными проблемными областями. [2] а также принципу открытости-закрытости , позволяя расширять функциональность класса без изменения. [3] Использование декоратора может быть более эффективным, чем создание подклассов, поскольку поведение объекта можно улучшить без определения совершенно нового объекта.
Обзор
[ редактировать ]Декоратор [4] шаблон проектирования — один из двадцати трех известных шаблонов проектирования ; в них описывается, как решать повторяющиеся проблемы проектирования и разрабатывать гибкое и повторно используемое объектно-ориентированное программное обеспечение, то есть объекты, которые легче реализовать, изменить, протестировать и повторно использовать.
Какие проблемы он может решить?
[ редактировать ]- Обязанности следует добавлять к объекту (и удалять из него) динамически во время выполнения. [5]
- Должна быть предоставлена гибкая альтернатива созданию подклассов для расширения функциональности.
При использовании подклассов разные подклассы расширяют класс по-разному. Но расширение привязывается к классу во время компиляции и не может быть изменено во время выполнения. [ нужна ссылка ]
Какое решение оно описывает?
[ редактировать ]Определять Decorator
объекты, которые
- реализовать интерфейс расширенного (оформленного) объекта (
Component
) прозрачно, пересылая на него все запросы - выполнять дополнительный функционал до/после пересылки запроса.
Это позволяет работать с разными Decorator
объекты для динамического расширения функциональности объекта во время выполнения.
См. также класс UML и диаграмму последовательности ниже.
Намерение
[ редактировать ]
Шаблон декоратора может использоваться для расширения (украшения) функциональности определенного объекта статически или, в некоторых случаях, во время выполнения , независимо от других экземпляров того же класса , при условии, что некоторая подготовительная работа будет сделана во время разработки. Это достигается путем разработки нового класса Decorator , который является оболочкой исходного класса. Такая упаковка может быть достигнута с помощью следующей последовательности шагов:
- Подкласс исходного класса Component в класс Decorator (см. диаграмму UML);
- В классе Decorator добавьте указатель на компонент в качестве поля;
- В классе Decorator передайте компонент конструктору Decorator, чтобы инициализировать указатель компонента ;
- В Decorator классе перенаправьте все методы Component на указатель Component ; и
- В классе ConcreteDecorator переопределите любой метод компонента , поведение которого необходимо изменить.
Этот шаблон разработан таким образом, что несколько декораторов можно накладывать друг на друга, каждый раз добавляя новую функциональность к переопределенным методам.
Обратите внимание, что декораторы и исходный объект класса имеют общий набор функций. На предыдущей диаграмме метод Operation() был доступен как в декорированной, так и в недекорированной версии.
Функции оформления (например, методы, свойства или другие члены) обычно определяются интерфейсом, примесью (также известной как черта ) или наследованием класса, которое является общим для декораторов и декорируемого объекта. В предыдущем примере класс Component наследуется как ConcreteComponent, так и подклассами, которые происходят от Decorator .
Шаблон декоратора является альтернативой созданию подклассов . Создание подклассов добавляет поведение во время компиляции , и это изменение влияет на все экземпляры исходного класса; декорирование может обеспечить новое поведение во время выполнения выбранных объектов .
Это различие становится наиболее важным, когда существует несколько независимых способов расширения функциональности. В некоторых объектно-ориентированных языках программирования классы не могут создаваться во время выполнения, и во время разработки обычно невозможно предсказать, какие комбинации расширений потребуются. Это означало бы, что для каждой возможной комбинации придется создать новый класс. Напротив, декораторы — это объекты, созданные во время выполнения, и их можно комбинировать для каждого использования. Реализации потоков ввода-вывода как в Java , так и в .NET Framework включают шаблон декоратора.
Мотивация
[ редактировать ]
В качестве примера рассмотрим окно в оконной системе . Чтобы разрешить прокрутку содержимого окна, можно при необходимости добавить к нему горизонтальные или вертикальные полосы прокрутки . Предположим, что окна представлены экземплярами интерфейса Window , и предположим, что этот класс не имеет функций для добавления полос прокрутки. Можно создать подкласс ScrollingWindow , который их предоставляет, или создать ScrollingWindowDecorator , который добавляет эту функциональность к существующим объектам Window . На данный момент любое решение будет хорошо.
Теперь предположим, что вам также нужна возможность добавлять границы к окнам. Опять же, исходный класс Window не имеет поддержки. Подкласс ScrollingWindow теперь представляет собой проблему, поскольку он фактически создал новый тип окна. Если вы хотите добавить поддержку границ для многих, но не для всех окон, необходимо создать подклассы WindowWithBorder и ScrollingWindowWithBorder и т. д. Эта проблема усугубляется с каждой новой функцией или подтипом окна, которые будут добавлены. Для решения декоратора новый BorderedWindowDecorator создается . Любая комбинация ScrollingWindowDecorator или BorderedWindowDecorator может украшать существующие окна. Если функциональность необходимо добавить во все Windows, базовый класс можно изменить. С другой стороны, иногда (например, при использовании внешних фреймворков) модифицировать базовый класс невозможно, законно или удобно.
В предыдущем примере классы SimpleWindow и WindowDecorator реализуют интерфейс Window , который определяет методы draw() и getDescription(), необходимые в этом сценарии для оформления оконного элемента управления.
Распространенные варианты использования
[ редактировать ]Применение декораторов
[ редактировать ]Добавление или удаление декораторов по команде (например, по нажатию кнопки) — это распространенный шаблон пользовательского интерфейса, часто реализуемый вместе с шаблоном проектирования «Команда» . Например, приложение для редактирования текста может иметь кнопку для выделения текста. При нажатии кнопки отдельные выбранные в данный момент текстовые глифы будут заключены в декораторы, которые изменяют их функцию draw(), заставляя их рисоваться выделенным образом (реальная реализация, вероятно, также будет использовать систему демаркации для максимизации эффективности).
Применение или удаление декораторов на основе изменений состояния — еще один распространенный вариант использования. В зависимости от объёма состояния декораторы могут применяться или удаляться массово. Аналогично, шаблон проектирования «Состояние» может быть реализован с использованием декораторов вместо объектов-подклассов, инкапсулирующих изменяющуюся функциональность. Использование декораторов таким образом делает внутреннее состояние и функциональность объекта State более композиционным и способным обрабатывать произвольную сложность.
Использование в объектах-легковесах
[ редактировать ]Украшение также часто используется в шаблоне дизайна «Легкий вес» . Объекты-легковесы делятся на два компонента: инвариантный компонент, который является общим для всех объектов-легковесов; и вариант декорированного компонента, который может быть частично общим или полностью необщим. Такое разделение объекта-приспособленца предназначено для уменьшения потребления памяти. Декораторы обычно также кэшируются и используются повторно. Все декораторы будут содержать общую ссылку на общий инвариантный объект. Если декорированное состояние является лишь частично вариантным, то декораторы также могут быть в некоторой степени общими, хотя необходимо соблюдать осторожность, чтобы не изменить их состояние во время их использования. UITableView в iOS реализует шаблон навесного веса таким образом: многоразовые ячейки табличного представления являются декораторами, которые содержат ссылки на общий объект строки табличного представления, а ячейки кэшируются/повторно используются.
Препятствия взаимодействия с декораторами
[ редактировать ]Применение комбинаций декораторов различными способами к коллекции объектов создает некоторые проблемы при взаимодействии с коллекцией таким образом, чтобы в полной мере использовать преимущества функциональности, добавляемой декораторами. использование шаблонов «Адаптер» или «Посетитель» В таких случаях может быть полезно . Взаимодействие с несколькими уровнями декораторов создает дополнительные проблемы, и логика адаптеров и посетителей должна быть разработана с учетом этого.
Архитектурная значимость
[ редактировать ]Декораторы поддерживают композиционный , а не нисходящий иерархический подход к расширению функциональности. Декоратор позволяет добавлять или изменять поведение интерфейса во время выполнения. Их можно использовать для обертывания объектов произвольной многослойной комбинацией способов. Проделать то же самое с подклассами означает реализовать сложные сети множественного наследования, которые неэффективны с точки зрения использования памяти и в определенный момент просто не могут масштабироваться. Аналогично, попытка реализовать ту же функциональность со свойствами приводит к раздуванию каждого экземпляра объекта ненужными свойствами. По вышеуказанным причинам декораторы часто считаются эффективной альтернативой созданию подклассов с точки зрения использования памяти.
Декораторы также можно использовать для специализации объектов, которые не являются подклассами, характеристики которых необходимо изменять во время выполнения (как упоминалось в другом месте), или вообще объектов, которым не хватает некоторых необходимых функций.
Использование для улучшения API
[ редактировать ]Шаблон декоратора также может дополнять шаблон Фасад . Фасад предназначен для простого взаимодействия со сложной системой, которую он инкапсулирует, но не добавляет функциональности системе. Однако упаковка сложной системы обеспечивает пространство, которое можно использовать для внедрения новых функций, основанных на координации подкомпонентов в системе. Например, шаблон фасада может объединить словари многих разных языков в одном интерфейсе многоязычных словарей. Новый интерфейс может также предоставлять новые функции для перевода слов между языками. Это гибридная модель: унифицированный интерфейс предоставляет пространство для расширения. Думайте о декораторах как о не ограничивающихся обертыванием отдельных объектов, но способных также обертывать кластеры объектов в этом гибридном подходе.
Альтернативы декораторам
[ редактировать ]В качестве альтернативы шаблону декоратора можно использовать адаптер , когда оболочка должна соблюдать определенный интерфейс и поддерживать полиморфное поведение, а также фасад, когда требуется более легкий или простой интерфейс к базовому объекту. [6]
Шаблон | Намерение |
---|---|
Адаптер | Преобразует один интерфейс в другой, чтобы он соответствовал ожиданиям клиента. |
Декоратор | Динамически добавляет ответственности к интерфейсу путем переноса исходного кода. |
Фасад | Обеспечивает упрощенный интерфейс |
Структура
[ редактировать ]Класс 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
.
Примеры
[ редактировать ]С++
[ редактировать ]Эта реализация основана на реализации до C++98, описанной в книге.
#include <iostream>
#include <memory>
// Beverage interface.
class Beverage {
public:
virtual void drink() = 0;
virtual ~Beverage() = default;
};
// Drinks which can be decorated.
class Coffee : public Beverage {
public:
virtual void drink() override {
std::cout << "Drinking Coffee";
}
};
class Soda : public Beverage {
public:
virtual void drink() override {
std::cout << "Drinking Soda";
}
};
class BeverageDecorator : public Beverage {
public:
BeverageDecorator() = delete;
BeverageDecorator(std::unique_ptr<Beverage> component_)
: component(std::move(component_))
{}
virtual void drink() = 0;
protected:
void callComponentDrink() {
if (component) {
component->drink();
}
}
private:
std::unique_ptr<Beverage> component;
};
class Milk : public BeverageDecorator {
public:
Milk(std::unique_ptr<Beverage> component_, float percentage_)
: BeverageDecorator(std::move(component_))
, percentage(percentage_)
{
}
virtual void drink() override {
callComponentDrink();
std::cout << ", with milk of richness " << percentage << "%";
}
private:
float percentage;
};
class IceCubes : public BeverageDecorator {
public:
IceCubes(std::unique_ptr<Beverage> component_, int count_)
: BeverageDecorator(std::move(component_))
, count(count_)
{
}
virtual void drink() override {
callComponentDrink();
std::cout << ", with " << count << " ice cubes";
}
private:
int count;
};
class Sugar : public BeverageDecorator {
public:
Sugar(std::unique_ptr<Beverage> component_, int spoons_)
: BeverageDecorator(std::move(component_))
, spoons(spoons_)
{
}
virtual void drink() override {
callComponentDrink();
std::cout << ", with " << spoons << " spoons of sugar";
}
private:
int spoons = 1;
};
int main() {
std::unique_ptr<Beverage> soda = std::make_unique<Soda>();
soda = std::make_unique<IceCubes>(std::move(soda), 3);
soda = std::make_unique<Sugar>(std::move(soda), 1);
soda->drink();
std::cout << std::endl;
std::unique_ptr<Beverage> coffee = std::make_unique<Coffee>();
coffee = std::make_unique<IceCubes>(std::move(coffee), 16);
coffee = std::make_unique<Milk>(std::move(coffee), 3.);
coffee = std::make_unique<Sugar>(std::move(coffee), 2);
coffee->drink();
return 0;
}
Вывод программы такой
Drinking Soda, with 3 ice cubes, with 1 spoons of sugar
Drinking Coffee, with 16 ice cubes, with milk of richness 3%, with 2 spoons of sugar
Полный пример можно протестировать на странице godbolt .
С++
[ редактировать ]Здесь представлены два варианта: во-первых, динамический, компонуемый во время выполнения декоратор (имеет проблемы с вызовом декорированных функций, если они не проксированы явно) и декоратор, использующий наследование миксинов.
Динамический декоратор
[ редактировать ]#include <iostream>
#include <string>
struct Shape {
virtual ~Shape() = default;
virtual std::string GetName() const = 0;
};
struct Circle : Shape {
void Resize(float factor) { radius *= factor; }
std::string GetName() const override {
return std::string("A circle of radius ") + std::to_string(radius);
}
float radius = 10.0f;
};
struct ColoredShape : Shape {
ColoredShape(const std::string& color, Shape* shape)
: color(color), shape(shape) {}
std::string GetName() const override {
return shape->GetName() + " which is colored " + color;
}
std::string color;
Shape* shape;
};
int main() {
Circle circle;
ColoredShape colored_shape("red", &circle);
std::cout << colored_shape.GetName() << std::endl;
}
#include <memory>
#include <iostream>
#include <string>
struct WebPage
{
virtual void display()=0;
virtual ~WebPage() = default;
};
struct BasicWebPage : WebPage
{
std::string html;
void display() override
{
std::cout << "Basic WEB page" << std::endl;
}
};
struct WebPageDecorator : WebPage
{
WebPageDecorator(std::unique_ptr<WebPage> webPage): _webPage(std::move(webPage))
{
}
void display() override
{
_webPage->display();
}
private:
std::unique_ptr<WebPage> _webPage;
};
struct AuthenticatedWebPage : WebPageDecorator
{
AuthenticatedWebPage(std::unique_ptr<WebPage> webPage):
WebPageDecorator(std::move(webPage))
{}
void authenticateUser()
{
std::cout << "authentification done" << std::endl;
}
void display() override
{
authenticateUser();
WebPageDecorator::display();
}
};
struct AuthorizedWebPage : WebPageDecorator
{
AuthorizedWebPage(std::unique_ptr<WebPage> webPage):
WebPageDecorator(std::move(webPage))
{}
void authorizedUser()
{
std::cout << "authorized done" << std::endl;
}
void display() override
{
authorizedUser();
WebPageDecorator::display();
}
};
int main(int argc, char* argv[])
{
std::unique_ptr<WebPage> myPage = std::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;
return 0;
}
Статический декоратор (наследование миксинов)
[ редактировать ]В этом примере демонстрируется статическая реализация Decorator, которая возможна благодаря способности C++ наследовать аргумент шаблона.
#include <iostream>
#include <string>
struct Circle {
void Resize(float factor) { radius *= factor; }
std::string GetName() const {
return std::string("A circle of radius ") + std::to_string(radius);
}
float radius = 10.0f;
};
template <typename T>
struct ColoredShape : public T {
ColoredShape(const std::string& color) : color(color) {}
std::string GetName() const {
return T::GetName() + " which is colored " + color;
}
std::string color;
};
int main() {
ColoredShape<Circle> red_circle("red");
std::cout << red_circle.GetName() << std::endl;
red_circle.Resize(1.5f);
std::cout << red_circle.GetName() << std::endl;
}
Ява
[ редактировать ]Первый пример (сценарий окна/прокрутки)
[ редактировать ]Следующий пример Java иллюстрирует использование декораторов в сценарии окна/прокрутки.
// The Window interface class
public interface Window {
void draw(); // Draws the Window
String getDescription(); // Returns a description of the Window
}
// Implementation of a simple Window without any scrollbars
class SimpleWindow implements Window {
@Override
public void draw() {
// Draw window
}
@Override
public String getDescription() {
return "simple window";
}
}
Следующие классы содержат декораторы для всех Window
классы, включая сами классы декораторов.
// abstract decorator class - note that it implements Window
abstract class WindowDecorator implements Window {
private final Window windowToBeDecorated; // the Window being decorated
public WindowDecorator (Window windowToBeDecorated) {
this.windowToBeDecorated = windowToBeDecorated;
}
@Override
public void draw() {
windowToBeDecorated.draw(); //Delegation
}
@Override
public String getDescription() {
return windowToBeDecorated.getDescription(); //Delegation
}
}
// The first concrete decorator which adds vertical scrollbar functionality
class VerticalScrollBarDecorator extends WindowDecorator {
public VerticalScrollBarDecorator (Window windowToBeDecorated) {
super(windowToBeDecorated);
}
@Override
public void draw() {
super.draw();
drawVerticalScrollBar();
}
private void drawVerticalScrollBar() {
// Draw the vertical scrollbar
}
@Override
public String getDescription() {
return super.getDescription() + ", including vertical scrollbars";
}
}
// The second concrete decorator which adds horizontal scrollbar functionality
class HorizontalScrollBarDecorator extends WindowDecorator {
public HorizontalScrollBarDecorator (Window windowToBeDecorated) {
super(windowToBeDecorated);
}
@Override
public void draw() {
super.draw();
drawHorizontalScrollBar();
}
private void drawHorizontalScrollBar() {
// Draw the horizontal scrollbar
}
@Override
public String getDescription() {
return super.getDescription() + ", including horizontal scrollbars";
}
}
Вот тестовая программа, которая создает Window
экземпляр, который полностью оформлен (т.е. с вертикальными и горизонтальными полосами прокрутки) и печатает свое описание:
public class DecoratedWindowTest {
public static void main(String[] args) {
// Create a decorated Window with horizontal and vertical scrollbars
Window decoratedWindow = new HorizontalScrollBarDecorator (
new VerticalScrollBarDecorator (new SimpleWindow()));
// Print the Window's description
System.out.println(decoratedWindow.getDescription());
}
}
Результатом работы этой программы является «простое окно, включая вертикальные полосы прокрутки, включая горизонтальные полосы прокрутки». Обратите внимание, как getDescription
метод двух декораторов сначала извлекает декорированный Window
описание и украшает его суффиксом.
Ниже приведен тестовый класс JUnit для разработки через тестирование.
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class WindowDecoratorTest {
@Test
public void testWindowDecoratorTest() {
Window decoratedWindow = new HorizontalScrollBarDecorator(new VerticalScrollBarDecorator(new SimpleWindow()));
// assert that the description indeed includes horizontal + vertical scrollbars
assertEquals("simple window, including vertical scrollbars, including horizontal scrollbars", decoratedWindow.getDescription());
}
}
Второй пример (сценарий приготовления кофе)
[ редактировать ]Следующий пример Java иллюстрирует использование декораторов в сценарии приготовления кофе. В этом примере сценарий включает только стоимость и ингредиенты.
// The interface Coffee defines the functionality of Coffee implemented by decorator
public interface Coffee {
public double getCost(); // Returns the cost of the coffee
public String getIngredients(); // Returns the ingredients of the coffee
}
// Extension of a simple coffee without any extra ingredients
public class SimpleCoffee implements Coffee {
@Override
public double getCost() {
return 1;
}
@Override
public String getIngredients() {
return "Coffee";
}
}
Следующие классы содержат декораторы для всех Кофе- классы, включая сами занятия декораторов.
// Abstract decorator class - note that it implements Coffee interface
public abstract class CoffeeDecorator implements Coffee {
private final Coffee decoratedCoffee;
public CoffeeDecorator(Coffee c) {
this.decoratedCoffee = c;
}
@Override
public double getCost() { // Implementing methods of the interface
return decoratedCoffee.getCost();
}
@Override
public String getIngredients() {
return decoratedCoffee.getIngredients();
}
}
// Decorator WithMilk mixes milk into coffee.
// Note it extends CoffeeDecorator.
class WithMilk extends CoffeeDecorator {
public WithMilk(Coffee c) {
super(c);
}
@Override
public double getCost() { // Overriding methods defined in the abstract superclass
return super.getCost() + 0.5;
}
@Override
public String getIngredients() {
return super.getIngredients() + ", Milk";
}
}
// Decorator WithSprinkles mixes sprinkles onto coffee.
// Note it extends CoffeeDecorator.
class WithSprinkles extends CoffeeDecorator {
public WithSprinkles(Coffee c) {
super(c);
}
@Override
public double getCost() {
return super.getCost() + 0.2;
}
@Override
public String getIngredients() {
return super.getIngredients() + ", Sprinkles";
}
}
Вот тестовая программа, которая создает Экземпляр кофе , полностью украшенный (молоком и посыпкой), рассчитанный на стоимость кофе и распечатывающий его ингредиенты:
public class Main {
public static void printInfo(Coffee c) {
System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
}
public static void main(String[] args) {
Coffee c = new SimpleCoffee();
printInfo(c);
c = new WithMilk(c);
printInfo(c);
c = new WithSprinkles(c);
printInfo(c);
}
}
Вывод этой программы приведен ниже:
Cost: 1.0; Ingredients: Coffee Cost: 1.5; Ingredients: Coffee, Milk Cost: 1.7; Ingredients: Coffee, Milk, Sprinkles
PHP
[ редактировать ]abstract class Component
{
protected $data;
protected $value;
abstract public function getData();
abstract public function getValue();
}
class ConcreteComponent extends Component
{
public function __construct()
{
$this->value = 1000;
$this->data = "Concrete Component:\t{$this->value}\n";
}
public function getData()
{
return $this->data;
}
public function getValue()
{
return $this->value;
}
}
abstract class Decorator extends Component
{
}
class ConcreteDecorator1 extends Decorator
{
public function __construct(Component $data)
{
$this->value = 500;
$this->data = $data;
}
public function getData()
{
return $this->data->getData() . "Concrete Decorator 1:\t{$this->value}\n";
}
public function getValue()
{
return $this->value + $this->data->getValue();
}
}
class ConcreteDecorator2 extends Decorator
{
public function __construct(Component $data)
{
$this->value = 500;
$this->data = $data;
}
public function getData()
{
return $this->data->getData() . "Concrete Decorator 2:\t{$this->value}\n";
}
public function getValue()
{
return $this->value + $this->data->getValue();
}
}
class Client
{
private $component;
public function __construct()
{
$this->component = new ConcreteComponent();
$this->component = $this->wrapComponent($this->component);
echo $this->component->getData();
echo "Client:\t\t\t";
echo $this->component->getValue();
}
private function wrapComponent(Component $component)
{
$component1 = new ConcreteDecorator1($component);
$component2 = new ConcreteDecorator2($component1);
return $component2;
}
}
$client = new Client();
// Result: #quanton81
//Concrete Component: 1000
//Concrete Decorator 1: 500
//Concrete Decorator 2: 500
//Client: 2000
Питон
[ редактировать ]Следующий пример Python, взятый из Python Wiki — DecoratorPattern , показывает нам, как конвейеризировать декораторы для динамического добавления множества вариантов поведения в объект:
"""
Demonstrated decorators in a world of a 10x10 grid of values 0-255.
"""
import random
def s32_to_u16(x):
if x < 0:
sign = 0xF000
else:
sign = 0
bottom = x & 0x00007FFF
return bottom | sign
def seed_from_xy(x, y):
return s32_to_u16(x) | (s32_to_u16(y) << 16)
class 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
random.seed(seed)
return random.randint(0, 255)
class DataSquare:
def __init__(s, initial_value=None):
s.data = [initial_value] * 10 * 10
def get(s, x, y):
return s.data[(y * 10) + x] # yes: these are all 10x10
def set(s, x, y, u):
s.data[(y * 10) + x] = u
class CacheDecorator:
def __init__(s, decorated):
s.decorated = decorated
s.cache = DataSquare()
def get(s, x, y):
if s.cache.get(x, y) == None:
s.cache.set(x, y, s.decorated.get(x, y))
return s.cache.get(x, y)
class MaxDecorator:
def __init__(s, decorated, max):
s.decorated = decorated
s.max = max
def get(s, x, y):
if s.decorated.get(x, y) > s.max:
return s.max
return s.decorated.get(x, y)
class MinDecorator:
def __init__(s, decorated, min):
s.decorated = decorated
s.min = min
def get(s, x, y):
if s.decorated.get(x, y) < s.min:
return s.min
return s.decorated.get(x, y)
class VisibilityDecorator:
def __init__(s, decorated):
s.decorated = decorated
def get(s, x, y):
return s.decorated.get(x, y)
def draw(s):
for y in range(10):
for x in range(10):
print "%3d" % s.get(x, y),
print
# Now, build up a pipeline of decorators:
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)
final.draw()
Примечание:
Шаблон «Декоратор» (или реализацию этого шаблона проектирования в Python — как в приведенном выше примере) не следует путать с декораторами Python , языковой функцией Python. Это разные вещи.
Второй после Python Wiki:
Шаблон «Декоратор» — это шаблон, описанный в Книге шаблонов проектирования. Это способ очевидного изменения поведения объекта путем включения его в декоративный объект с аналогичным интерфейсом. Не следует путать с декораторами Python, которые представляют собой функцию языка для динамического изменения функции или класса. [8]
Кристалл
[ редактировать ]abstract class Coffee
abstract def cost
abstract def ingredients
end
# Extension of a simple coffee
class SimpleCoffee < Coffee
def cost
1.0
end
def ingredients
"Coffee"
end
end
# Abstract decorator
class CoffeeDecorator < Coffee
protected getter decorated_coffee : Coffee
def initialize(@decorated_coffee)
end
def cost
decorated_coffee.cost
end
def ingredients
decorated_coffee.ingredients
end
end
class WithMilk < CoffeeDecorator
def cost
super + 0.5
end
def ingredients
super + ", Milk"
end
end
class WithSprinkles < CoffeeDecorator
def cost
super + 0.2
end
def ingredients
super + ", Sprinkles"
end
end
class Program
def print(coffee : Coffee)
puts "Cost: #{coffee.cost}; Ingredients: #{coffee.ingredients}"
end
def initialize
coffee = SimpleCoffee.new
print(coffee)
coffee = WithMilk.new(coffee)
print(coffee)
coffee = WithSprinkles.new(coffee)
print(coffee)
end
end
Program.new
Выход:
Cost: 1.0; Ingredients: Coffee Cost: 1.5; Ingredients: Coffee, Milk Cost: 1.7; Ingredients: Coffee, Milk, Sprinkles
С#
[ редактировать ]namespace WikiDesignPatterns;
public interface IBike
{
string GetDetails();
double GetPrice();
}
public class AluminiumBike : IBike
{
public double GetPrice() =>
100.0;
public string GetDetails() =>
"Aluminium Bike";
}
public class CarbonBike : IBike
{
public double GetPrice() =>
1000.0;
public string GetDetails() =>
"Carbon";
}
public abstract class BikeAccessories : IBike
{
private readonly IBike _bike;
public BikeAccessories(IBike bike)
{
_bike = bike;
}
public virtual double GetPrice() =>
_bike.GetPrice();
public virtual string GetDetails() =>
_bike.GetDetails();
}
public class SecurityPackage : BikeAccessories
{
public SecurityPackage(IBike bike):base(bike)
{
}
public override string GetDetails() =>
base.GetDetails() + " + Security Package";
public override double GetPrice() =>
base.GetPrice() + 1;
}
public class SportPackage : BikeAccessories
{
public SportPackage(IBike bike) : base(bike)
{
}
public override string GetDetails() =>
base.GetDetails() + " + Sport Package";
public override double GetPrice() =>
base.GetPrice() + 10;
}
public class BikeShop
{
public static void UpgradeBike()
{
var basicBike = new AluminiumBike();
BikeAccessories upgraded = new SportPackage(basicBike);
upgraded = new SecurityPackage(upgraded);
Console.WriteLine($"Bike: '{upgraded.GetDetails()}' Cost: {upgraded.GetPrice()}");
}
}
Выход:
Bike: 'Aluminium Bike + Sport Package + Security Package' Cost: 111
Руби
[ редактировать ]class AbstractCoffee
def print
puts "Cost: #{cost}; Ingredients: #{ingredients}"
end
end
class SimpleCoffee < AbstractCoffee
def cost
1.0
end
def ingredients
"Coffee"
end
end
class WithMilk < SimpleDelegator
def cost
__getobj__.cost + 0.5
end
def ingredients
__getobj__.ingredients + ", Milk"
end
end
class WithSprinkles < SimpleDelegator
def cost
__getobj__.cost + 0.2
end
def ingredients
__getobj__.ingredients + ", Sprinkles"
end
end
coffee = SimpleCoffee.new
coffee.print
coffee = WithMilk.new(coffee)
coffee.print
coffee = WithSprinkles.new(coffee)
coffee.print
Выход:
Cost: 1.0; Ingredients: Coffee Cost: 1.5; Ingredients: Coffee, Milk Cost: 1.7; Ingredients: Coffee, Milk, Sprinkles
См. также
[ редактировать ]- Составной узор
- Шаблон адаптера
- Абстрактный класс
- Абстрактная фабрика
- Аспектно-ориентированное программирование
- Неизменяемый объект
Ссылки
[ редактировать ]- ^ Гамма, Эрих; и др. (1995). Шаблоны проектирования . Ридинг, Массачусетс: Addison-Wesley Publishing Co, Inc., стр. 175 и далее . ISBN 0-201-63361-2 .
- ^ «Как реализовать шаблон декоратора» . Архивировано из оригинала 7 июля 2015 г.
- ^ «Шаблон декоратора, почему мы перестали его использовать и альтернатива» . 8 марта 2022 г.
- ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Эддисон Уэсли. стр. 175 и далее . ISBN 0-201-63361-2 .
{{cite book}}
: CS1 maint: несколько имен: список авторов ( ссылка ) - ^ «Шаблон проектирования «Декоратор» — проблема, решение и применимость» . w3sDesign.com . Проверено 12 августа 2017 г.
- ^ Фриман, Эрик; Фриман, Элизабет; Сьерра, Кэти; Бейтс, Берт (2004). Хендриксон, Майк; Лукидес, Майк (ред.). Шаблоны проектирования Head First (мягкая обложка) . Том. 1. О'Рейли. стр. 243, 252, 258, 260. ISBN. 978-0-596-00712-6 . Проверено 2 июля 2012 г.
- ^ «Шаблон проектирования «Декоратор» — структура и взаимодействие» . w3sDesign.com . Проверено 12 августа 2017 г.
- ^ «DecoratorPattern — Python Wiki» . wiki.python.org .
Внешние ссылки
[ редактировать ]