Декоратор (Decorator) — различия между версиями
Admin (обсуждение | вклад) (→Назначение) |
Admin (обсуждение | вклад) (→Назначение) |
||
(не показано 14 промежуточных версий этого же участника) | |||
Строка 8: | Строка 8: | ||
=== Описание === | === Описание === | ||
+ | В некоторых ситуациях необходимо возложить дополнительные обязанности на объект, а не на класс в целом. Решения на основе наследования - статические, т.е. принимаемые на этапе компиляции, и поэтому не являются достаточно гибкими. | ||
+ | |||
+ | Паттерн декоратор предлагает следующий подход: поместить компонент в другой объект, называемый Декоратором, который как раз и добавляет новые свойства. | ||
+ | |||
+ | Декоратор удовлетворяет интерфейсу исходного объекта, поэтому отдекорированный объект можно использовать как исходный. Декоратор переадресует запросы внутреннему компоненту и может выполнять дополнительные (декорирующие) действия. | ||
+ | |||
+ | Декораторы могут вкладываться друг в друга, добавляя любое количество новых свойств. | ||
+ | |||
+ | === Использование === | ||
+ | Декоратор предназначен: | ||
+ | * для динамического добавления обязанностей объектам | ||
+ | * для реализации обязанностей, которые могут быть сняты с объекта | ||
+ | * когда расширение путем порождения подклассов неудобно | ||
=== Реализация === | === Реализация === | ||
==== Диаграмма классов ==== | ==== Диаграмма классов ==== | ||
− | [[Изображение: | + | [[Изображение:DecoratorCommon.png]] |
==== Участники==== | ==== Участники==== | ||
− | * | + | * '''Component''' - компонент |
+ | Определяет интерфейс для объектов, которые могут быть отдекорированы | ||
+ | * '''ConcreteComponent''' - конкретный компонент | ||
+ | Реализует интерфейс Component | ||
+ | * '''Decorator''' - декоратор | ||
+ | Хранит ссылку на декорируемый объект Component и реализует интерфейс Component, вызывая соответствующие методы декорируемого объекта (поведение по умолчанию для декораторов) | ||
+ | * '''ConcreteDecorator''' - конкретный декоратор | ||
+ | В методах, реализующих интерфейс Component, добавляет некоторую функциональность (декор) | ||
=== Пример === | === Пример === | ||
+ | Пример. Файловые потоки в 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)
Назначение
Динамически добавляет объекту новые свойства (без использования наследования, на этапе выполнения).
Описание
В некоторых ситуациях необходимо возложить дополнительные обязанности на объект, а не на класс в целом. Решения на основе наследования - статические, т.е. принимаемые на этапе компиляции, и поэтому не являются достаточно гибкими.
Паттерн декоратор предлагает следующий подход: поместить компонент в другой объект, называемый Декоратором, который как раз и добавляет новые свойства.
Декоратор удовлетворяет интерфейсу исходного объекта, поэтому отдекорированный объект можно использовать как исходный. Декоратор переадресует запросы внутреннему компоненту и может выполнять дополнительные (декорирующие) действия.
Декораторы могут вкладываться друг в друга, добавляя любое количество новых свойств.
Использование
Декоратор предназначен:
- для динамического добавления обязанностей объектам
- для реализации обязанностей, которые могут быть сняты с объекта
- когда расширение путем порождения подклассов неудобно
Реализация
Диаграмма классов
Участники
- 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()");
}
}
Достоинства и недостатки
- Позволяет настраивать поведение компонента во время выполнения программы
- Декораторы могут совместно использоваться несколькими объектами
- Недостаток: наличие большого количества маленьких декораторов снижает производительность и затрудняет отладку.
Варианты
- Удаление декораторов во время выполнения. Для этого - ссылки на предыдущий и следующий.
- Перекрывающиеся классы декораторов (часть функциональности совпадает). Следует корректно обрабатывать совпадение функциональности.