Jump to content

Шаблон декоратора

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

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

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

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

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

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

[ редактировать ]

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

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

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

Намерение

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

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

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

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

Обратите внимание, что декораторы и исходный объект класса имеют общий набор функций. На предыдущей диаграмме метод 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(), необходимые в этом сценарии для оформления оконного элемента управления.

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

[ редактировать ]

Применение декораторов

[ редактировать ]

Добавление или удаление декораторов по команде (например, по нажатию кнопки) — это распространенный шаблон пользовательского интерфейса, часто реализуемый вместе с шаблоном проектирования «Команда» . Например, приложение для редактирования текста может иметь кнопку для выделения текста. При нажатии кнопки отдельные выбранные в данный момент текстовые глифы будут заключены в декораторы, которые изменяют их функцию draw(), заставляя их рисоваться выделенным образом (реальная реализация, вероятно, также будет использовать систему демаркации для максимизации эффективности).

Применение или удаление декораторов на основе изменений состояния — еще один распространенный вариант использования. В зависимости от объёма состояния декораторы могут применяться или удаляться массово. Аналогично, шаблон проектирования «Состояние» может быть реализован с использованием декораторов вместо объектов-подклассов, инкапсулирующих изменяющуюся функциональность. Использование декораторов таким образом делает внутреннее состояние и функциональность объекта State более композиционным и способным обрабатывать произвольную сложность.

Использование в объектах-легковесах

[ редактировать ]

Украшение также часто используется в шаблоне дизайна «Легкий вес» . Объекты-легковесы делятся на два компонента: инвариантный компонент, который является общим для всех объектов-легковесов; и вариант декорированного компонента, который может быть частично общим или полностью необщим. Такое разделение объекта-приспособленца предназначено для уменьшения потребления памяти. Декораторы обычно также кэшируются и используются повторно. Все декораторы будут содержать общую ссылку на общий инвариантный объект. Если декорированное состояние является лишь частично вариантным, то декораторы также могут быть в некоторой степени общими, хотя необходимо соблюдать осторожность, чтобы не изменить их состояние во время их использования. UITableView в iOS реализует шаблон навесного веса таким образом: многоразовые ячейки табличного представления являются декораторами, которые содержат ссылки на общий объект строки табличного представления, а ячейки кэшируются/повторно используются.

Препятствия взаимодействия с декораторами

[ редактировать ]

Применение комбинаций декораторов различными способами к коллекции объектов создает некоторые проблемы при взаимодействии с коллекцией таким образом, чтобы в полной мере использовать преимущества функциональности, добавляемой декораторами. использование шаблонов «Адаптер» или «Посетитель» В таких случаях может быть полезно . Взаимодействие с несколькими уровнями декораторов создает дополнительные проблемы, и логика адаптеров и посетителей должна быть разработана с учетом этого.

Архитектурная значимость

[ редактировать ]

Декораторы поддерживают композиционный , а не нисходящий иерархический подход к расширению функциональности. Декоратор позволяет добавлять или изменять поведение интерфейса во время выполнения. Их можно использовать для обертывания объектов произвольной многослойной комбинацией способов. Проделать то же самое с подклассами означает реализовать сложные сети множественного наследования, которые неэффективны с точки зрения использования памяти и в определенный момент просто не могут масштабироваться. Аналогично, попытка реализовать ту же функциональность со свойствами приводит к раздуванию каждого экземпляра объекта ненужными свойствами. По вышеуказанным причинам декораторы часто считаются эффективной альтернативой созданию подклассов с точки зрения использования памяти.

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

Использование для улучшения API

[ редактировать ]

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

Альтернативы декораторам

[ редактировать ]

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

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

Структура

[ редактировать ]

Класс UML и диаграмма последовательности

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

На приведенной выше 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
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

См. также

[ редактировать ]
  1. ^ Гамма, Эрих; и др. (1995). Шаблоны проектирования . Ридинг, Массачусетс: Addison-Wesley Publishing Co, Inc., стр. 175 и далее . ISBN  0-201-63361-2 .
  2. ^ «Как реализовать шаблон декоратора» . Архивировано из оригинала 7 июля 2015 г.
  3. ^ «Шаблон декоратора, почему мы перестали его использовать и альтернатива» . 8 марта 2022 г.
  4. ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Эддисон Уэсли. стр. 175 и далее . ISBN  0-201-63361-2 . {{cite book}}: CS1 maint: несколько имен: список авторов ( ссылка )
  5. ^ «Шаблон проектирования «Декоратор» — проблема, решение и применимость» . w3sDesign.com . Проверено 12 августа 2017 г.
  6. ^ Фриман, Эрик; Фриман, Элизабет; Сьерра, Кэти; Бейтс, Берт (2004). Хендриксон, Майк; Лукидес, Майк (ред.). Шаблоны проектирования Head First (мягкая обложка) . Том. 1. О'Рейли. стр. 243, 252, 258, 260. ISBN.  978-0-596-00712-6 . Проверено 2 июля 2012 г.
  7. ^ «Шаблон проектирования «Декоратор» — структура и взаимодействие» . w3sDesign.com . Проверено 12 августа 2017 г.
  8. ^ «DecoratorPattern — Python Wiki» . wiki.python.org .
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: b9d86229e83013b5b5de8096ad54d536__1705966560
URL1:https://arc.ask3.ru/arc/aa/b9/36/b9d86229e83013b5b5de8096ad54d536.html
Заголовок, (Title) документа по адресу, URL1:
Decorator pattern - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)