Декоратор (Decorator) — различия между версиями

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск
(Описание)
(Назначение)
 
(не показано 11 промежуточных версий этого же участника)
Строка 9: Строка 9:
 
=== Описание ===
 
=== Описание ===
 
В некоторых ситуациях необходимо возложить дополнительные обязанности на объект, а не на класс в целом. Решения на основе наследования - статические, т.е. принимаемые на этапе компиляции, и поэтому не являются достаточно гибкими.
 
В некоторых ситуациях необходимо возложить дополнительные обязанности на объект, а не на класс в целом. Решения на основе наследования - статические, т.е. принимаемые на этапе компиляции, и поэтому не являются достаточно гибкими.
 +
 +
Паттерн декоратор предлагает следующий подход: поместить компонент в другой объект, называемый Декоратором, который как раз и добавляет новые свойства.
 +
 +
Декоратор удовлетворяет интерфейсу исходного объекта, поэтому отдекорированный объект можно использовать как исходный. Декоратор переадресует запросы внутреннему компоненту и может выполнять дополнительные (декорирующие) действия.
 +
 +
Декораторы могут вкладываться друг в друга, добавляя любое количество новых свойств.
 +
 +
=== Использование ===
 +
Декоратор предназначен:
 +
* для динамического добавления обязанностей объектам
 +
* для реализации обязанностей, которые могут быть сняты с объекта
 +
* когда расширение путем порождения подклассов неудобно
  
 
=== Реализация ===
 
=== Реализация ===
Строка 15: Строка 27:
  
 
==== Участники====
 
==== Участники====
*  
+
* '''Component''' - компонент
 +
Определяет интерфейс для объектов, которые могут быть отдекорированы
 +
* '''ConcreteComponent''' - конкретный компонент
 +
Реализует интерфейс Component
 +
* '''Decorator''' - декоратор
 +
Хранит ссылку на декорируемый объект Component и реализует интерфейс Component, вызывая соответствующие методы декорируемого объекта (поведение по умолчанию для декораторов)
 +
* '''ConcreteDecorator''' - конкретный декоратор
 +
В методах, реализующих интерфейс Component, добавляет некоторую функциональность (декор)
  
 
=== Пример ===
 
=== Пример ===
Пример. В Java - разновидности файловых потоков. К стандартному файловому потоку могут быть добавлены декораторы буферизации, шифрования, архивации и проч. в любых комбинациях.
+
Пример. Файловые потоки в C#.
 +
 
 +
<source lang="Csharp">
 +
Stream f = new FileStream(fileName, FileMode.Create);
 +
Stream gz = new GZipStream(f, CompressionMode.Compress);
 +
</source>
 +
или единым запросом:
 +
<source lang="Csharp">
 +
Stream gz = new GZipStream(new FileStream(fileName, FileMode.Create), CompressionMode.Compress);
 +
</source>
  
Декоратор принимает отдекорированный предыдущими декораторами объект и возвращает объект с дополнительной декорацией.
+
GZipStream декорирует поток, придавая ему дополнительное свойство сжатия. GZipStream, как и FileStream, наследуется от типа Stream.
  
Пример. Фримен. Кофе и дополнения: шоколад, ваниль, молоко, пена...
+
Среди других декораторов: BufferedStream, CryptoStream.
 +
 
 +
Хороший пример из книги Фримена. Кофе и дополнения: шоколад, ваниль, молоко, пена...
  
 
=== Код ===
 
=== Код ===
 
<source lang="Csharp">
 
<source lang="Csharp">
 +
class MainApp
 +
{
 +
    static void Main()
 +
    {
 +
        ConcreteComponent c = new ConcreteComponent();
 +
        ConcreteDecoratorA d1 = new ConcreteDecoratorA();
 +
        ConcreteDecoratorB d2 = new ConcreteDecoratorB();
 +
 +
        d1.SetComponent(c);
 +
        d2.SetComponent(d1);
 +
 +
        d2.Operation();
 +
    }
 +
}
 +
 +
abstract class Component
 +
{
 +
    public abstract void Operation();
 +
}
 +
 +
class ConcreteComponent : Component
 +
{
 +
    public override void Operation()
 +
    {
 +
        Console.WriteLine("ConcreteComponent.Operation()");
 +
    }
 +
}
 +
 +
abstract class Decorator : Component
 +
{
 +
    protected Component component;
 +
 +
    public void SetComponent(Component component)
 +
    {
 +
        this.component = component;
 +
    }
 +
 +
    public override void Operation()
 +
    {
 +
        if (component != null)
 +
        {
 +
            component.Operation();
 +
        }
 +
    }
 +
}
 +
 +
class ConcreteDecoratorA : Decorator
 +
{
 +
    public override void Operation()
 +
    {
 +
        base.Operation();
 +
        Console.WriteLine("ConcreteDecoratorA.Operation()");
 +
    }
 +
}
 +
 +
class ConcreteDecoratorB : Decorator
 +
{
 +
    public override void Operation()
 +
    {
 +
        base.Operation();
 +
        Console.WriteLine("ConcreteDecoratorB.Operation()");
 +
    }
 +
}
 
</source>
 
</source>
  
 
=== Достоинства и недостатки ===
 
=== Достоинства и недостатки ===
*  
+
* Позволяет настраивать поведение компонента во время выполнения программы
 +
* Декораторы могут совместно использоваться несколькими объектами
 +
* Недостаток: наличие большого количества маленьких декораторов снижает производительность и затрудняет отладку.
  
 
=== Варианты ===
 
=== Варианты ===
*
+
* Удаление декораторов во время выполнения. Для этого - ссылки на предыдущий и следующий.
 +
* Перекрывающиеся классы декораторов (часть функциональности совпадает). Следует корректно обрабатывать совпадение функциональности.

Текущая версия на 14:17, 27 августа 2014

К основной странице курса

Другое название

Обертка (Wrapper)

Назначение

Динамически добавляет объекту новые свойства (без использования наследования, на этапе выполнения).

Описание

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

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

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

Декораторы могут вкладываться друг в друга, добавляя любое количество новых свойств.

Использование

Декоратор предназначен:

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

Реализация

Диаграмма классов

DecoratorCommon.png

Участники

  • Component - компонент

Определяет интерфейс для объектов, которые могут быть отдекорированы

  • ConcreteComponent - конкретный компонент

Реализует интерфейс Component

  • Decorator - декоратор

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

  • ConcreteDecorator - конкретный декоратор

В методах, реализующих интерфейс Component, добавляет некоторую функциональность (декор)

Пример

Пример. Файловые потоки в C#.

Stream f = new FileStream(fileName, FileMode.Create);
Stream gz = new GZipStream(f, CompressionMode.Compress);

или единым запросом:

Stream gz = new GZipStream(new FileStream(fileName, FileMode.Create), CompressionMode.Compress);

GZipStream декорирует поток, придавая ему дополнительное свойство сжатия. GZipStream, как и FileStream, наследуется от типа Stream.

Среди других декораторов: BufferedStream, CryptoStream.

Хороший пример из книги Фримена. Кофе и дополнения: шоколад, ваниль, молоко, пена...

Код

class MainApp
{
    static void Main()
    {
        ConcreteComponent c = new ConcreteComponent();
        ConcreteDecoratorA d1 = new ConcreteDecoratorA();
        ConcreteDecoratorB d2 = new ConcreteDecoratorB();

        d1.SetComponent(c);
        d2.SetComponent(d1);

        d2.Operation();
    }
}

abstract class Component
{
    public abstract void Operation();
}

class ConcreteComponent : Component
{
    public override void Operation()
    {
        Console.WriteLine("ConcreteComponent.Operation()");
    }
}

abstract class Decorator : Component
{
    protected Component component;

    public void SetComponent(Component component)
    {
        this.component = component;
    }

    public override void Operation()
    {
        if (component != null)
        {
            component.Operation();
        }
    }
}

class ConcreteDecoratorA : Decorator
{
    public override void Operation()
    {
        base.Operation();
        Console.WriteLine("ConcreteDecoratorA.Operation()");
    }
}

class ConcreteDecoratorB : Decorator
{
    public override void Operation()
    {
        base.Operation();
        Console.WriteLine("ConcreteDecoratorB.Operation()");
    }
}

Достоинства и недостатки

  • Позволяет настраивать поведение компонента во время выполнения программы
  • Декораторы могут совместно использоваться несколькими объектами
  • Недостаток: наличие большого количества маленьких декораторов снижает производительность и затрудняет отладку.

Варианты

  • Удаление декораторов во время выполнения. Для этого - ссылки на предыдущий и следующий.
  • Перекрывающиеся классы декораторов (часть функциональности совпадает). Следует корректно обрабатывать совпадение функциональности.