Декоратор (шаблон проектирования): различия между версиями

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску
[непроверенная версия][непроверенная версия]
Содержимое удалено Содержимое добавлено
C++: Добавлена демонстрация того, что для применения изменений не обязательно вызывать операцию у конкретного декоратора, а достаточно вызвать у интерфейса.
 
(не показано 10 промежуточных версий 8 участников)
Строка 1: Строка 1:
{{Не путать|Python#Декораторы|декораторами функций или классов в Python|их концепция и концепция описываемого в этой статье шаблона проектирования отличаются}}
{{Значения|Декоратор}}
{{Значения|Декоратор}}
{{Шаблон проектирования
{{Шаблон проектирования
Строка 42: Строка 43:
* Базовые классы языка [[Java]] широко используют шаблон Декоратор для организации обработки операций ввода-вывода.
* Базовые классы языка [[Java]] широко используют шаблон Декоратор для организации обработки операций ввода-вывода.
* И декоратор, и [[Адаптер (шаблон проектирования)|адаптер]] являются обёртками вокруг объекта — хранят в себе [[Ссылка (программирование)|ссылку]] на оборачиваемый объект и часто передают в него вызовы методов. Отличие декоратора от адаптера в том, что адаптер имеет внешний интерфейс, отличный от интерфейса оборачиваемого объекта, и используется именно для стыковки разных интерфейсов. Декоратор же имеет точно такой же интерфейс, и используется для добавления функциональности.
* И декоратор, и [[Адаптер (шаблон проектирования)|адаптер]] являются обёртками вокруг объекта — хранят в себе [[Ссылка (программирование)|ссылку]] на оборачиваемый объект и часто передают в него вызовы методов. Отличие декоратора от адаптера в том, что адаптер имеет внешний интерфейс, отличный от интерфейса оборачиваемого объекта, и используется именно для стыковки разных интерфейсов. Декоратор же имеет точно такой же интерфейс, и используется для добавления функциональности.
* Для расширения функциональности класса возможно использовать как декораторы, так и [[Стратегия (шаблон проектирования)|стратегии]]. Декораторы оборачивают объект снаружи, стратегии же вставляются в него внутрь по неким интерфейсам.
* Для расширения функциональности класса возможно использовать как декораторы, так и [[Стратегия (шаблон проектирования)|стратегии]]. Декораторы оборачивают объект снаружи, стратегии же вставляются в него внутрь по неким интерфейсам.
** Недостаток стратегии: класс должен быть спроектирован с возможностью вставления стратегий, декоратор же не требует такой поддержки.
** Недостаток стратегии: класс должен быть спроектирован с возможностью вставления стратегий, декоратор же не требует такой поддержки.
** Недостаток декоратора: он оборачивает ровно тот же интерфейс, что предназначен для внешнего мира, что вызывает смешение публичного интерфейса и интерфейса кастомизации, которое не всегда желательно.
** Недостаток декоратора: он оборачивает ровно тот же интерфейс, что предназначен для внешнего мира, что вызывает смешение публичного интерфейса и интерфейса кастомизации, которое не всегда желательно.


Строка 52: Строка 53:


== Примеры ==
== Примеры ==

=== Kotlin ===
{{Скрытый блок|header=Пример на языке Kotlin|content=<source lang="kotlin">
fun main() {
LoggingNotifier(
FancyNotifier(
ConsoleNotifier()
)
).notify("Hello, World!")
}

interface Notifier {
fun notify(message: String)
}

class ConsoleNotifier : Notifier {
override fun notify(message: String) {
println(message)
}
}

class LoggingNotifier(private val notifier: Notifier) : Notifier {
override fun notify(message: String) {
notifier.notify(message)
println("LOG - $message") // Like a logger
}
}

class FancyNotifier(private val notifier: Notifier) : Notifier {
override fun notify(message: String) {
val border = "-".repeat(message.length)
notifier.notify("""
$border
$message
$border
""".trimIndent())
}
}
</source>}}


=== Ruby ===
=== Ruby ===
Строка 391: Строка 431:
=== C++ ===
=== C++ ===
{{Hider|
{{Hider|
title = '''Пример на языке С++''' |
title = '''Пример на языке C++''' |
content-style = text-align: left; |
content-style = text-align: left; |
hidden = true |
hidden = true |
Строка 435: Строка 475:
}
}
};
};

void Operation(IComponent &component) {
component.operation();
}
int main() {
int main() {
DecoratorTwo obj(std::make_shared<DecoratorOne>(std::make_shared<Component>()));
DecoratorTwo obj(std::make_shared<DecoratorOne>(std::make_shared<Component>()));
obj.operation(); // prints "Hello, World!\n"
Operation(obj); // prints "Hello, World!\n"
return 0;
return 0;
Строка 509: Строка 553:


=== Python ===
=== Python ===
Ниже — пример реализации шаблона проектирования. В Python существуют [[Python#Декораторы|декораторы функций и классов]], концепция которых отличается от концепции шаблона проектирования.
{{Hider|
{{Hider|
title = '''Пример на языке Python'''<ref>{{Cite web|url=https://wiki.python.org/moin/DecoratorPattern|title=Decorator Pattern|website=wiki.python.org|access-date=2021-10-24|archive-date=2021-10-24|archive-url=https://web.archive.org/web/20211024132841/https://wiki.python.org/moin/DecoratorPattern|deadlink=no}}</ref> |
title = '''Пример на языке Python''' |
content-style = text-align: left; |
content-style = text-align: left; |
hidden = true |
hidden = true |
content =
content =
<source lang="python">
<source lang="python3">
"""
from abc import ABCMeta, abstractmethod
Demonstrated decorators in a world of a 10x10 grid of values 0-255.
"""


import random
class IOperator(object):
"""
Интерфейс, который должны реализовать как декоратор,
так и оборачиваемый объект.
"""
__metaclass__ = ABCMeta


def s32_to_u16( x ):
@abstractmethod
def operator(self):
if x < 0:
pass
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 Component(IOperator):
class RandomSquare:
def __init__( s, seed_modifier ):
"""Компонент программы"""
s.seed_modifier = seed_modifier
def operator(self):
return 10.0
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 Wrapper(IOperator):
class CacheDecorator:
def __init__( s, decorated ):
"""Декоратор"""
s.decorated = decorated
def __init__(self, obj):
self.obj = obj
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 operator(self):
def __init__( s, decorated, max ):
return self.obj.operator() + 5.0
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:
comp = Component()
def __init__( s, decorated ):
comp = Wrapper(comp)
s.decorated = decorated
print comp.operator()
def get( s,x,y ):
# 15.0
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()
</source>

Выходные данные (учтите использование генератора псевдослучайных чисел):

<source lang="python3">
100 100 100 100 181 161 125 100 200 100
200 100 100 200 100 200 200 184 162 100
155 100 200 100 200 200 100 200 143 100
100 200 144 200 101 143 114 200 166 136
100 147 200 200 100 100 200 141 172 100
144 161 100 200 200 200 190 125 100 177
150 200 100 175 111 195 193 128 100 100
100 200 100 200 200 129 159 105 112 100
100 101 200 200 100 100 200 100 101 120
180 200 100 100 198 151 100 195 131 100
</source>
</source>
}}
}}
Строка 1107: Строка 1213:
ReadLn;
ReadLn;
end.
end.
</source>
}}

=== Swift ===
{{Hider|
title = '''Пример на языке Swift''' |
content-style = text-align: left; |
hidden = true |
content =
<source lang=swift>
protocol Book {
var title: String { get set }
var price: Int { get set }
func getPrice() -> Int
}

class BookImpl: Book {
var title: String = ""
var price: Int = 1000
func getPrice() -> Int {
return price
}
}

class DiscountBook: Book {
let element: BookImpl
var title: String = "«Грокаем алгоритмы»"
var price: Int = 0
init(element: BookImpl) {
self.element = element
self.title = element.title
self.price = element.price
}
// 30% sale
func getPrice() -> Int {
return price - (price * 30)/100
}
}

// Use Decorator

let book = BookImpl()
let discountBook = DiscountBook(element: book)
print(discountBook.getPrice())

</source>
</source>
}}
}}
Строка 1123: Строка 1282:
}}
}}
* {{Книга|автор=Эрик Фримен, Элизабет Фримен|заглавие=Паттерны проектирования = Head First Desing Patterns|ответственный=|издание=|место=СПб|издательство=Питер|год=|страницы=|страниц=656|isbn=978-5-459-00435-9}}
* {{Книга|автор=Эрик Фримен, Элизабет Фримен|заглавие=Паттерны проектирования = Head First Desing Patterns|ответственный=|издание=|место=СПб|издательство=Питер|год=|страницы=|страниц=656|isbn=978-5-459-00435-9}}

== Примечания ==
{{примечания}}


== Ссылки ==
== Ссылки ==
* [http://cpp-reference.ru/patterns/structural-patterns/decorator/ Паттерн Decorator (Декоратор)] — назначение, описание, особенности и реализация на С++.
* [http://cpp-reference.ru/patterns/structural-patterns/decorator/ Паттерн Decorator (Декоратор)] — назначение, описание, особенности и реализация на C++.


{{Типы шаблонов проектирования}}
{{Типы шаблонов проектирования}}
Строка 1131: Строка 1293:
[[Категория:Шаблоны проектирования]]
[[Категория:Шаблоны проектирования]]
[[Категория:Структурные шаблоны проектирования]]
[[Категория:Структурные шаблоны проектирования]]
[[Категория:Статьи с примерами кода Ruby]]

Текущая версия от 14:03, 13 февраля 2024

Декоратор
Decorator
Представление структуры шаблона Декоратор
Представление структуры шаблона Декоратор
Тип структурный
Назначение для динамического подключения к объекту дополнительных обязательств
Плюсы
  • нет необходимости создавать подклассы для расширения функциональности объекта;
  • возможность динамически подключать новую функциональность до или после основной функциональности объекта ConcreteComponent.
Родственные шаблоны Фасад, Адаптер
Описан в Design Patterns Да

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

Основные характеристики

[править | править код]

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

Способ решения

[править | править код]

Декоратор предусматривает расширение функциональности объекта без определения подклассов.

Класс ConcreteComponent — класс, в который с помощью шаблона Декоратор добавляется новая функциональность. В некоторых случаях базовая функциональность предоставляется классами, производными от класса ConcreteComponent. В подобных случаях класс ConcreteComponent является уже не конкретным, а абстрактным. Абстрактный класс Component определяет интерфейс для использования всех этих классов.

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

Реализация

[править | править код]

Создаётся абстрактный класс, представляющий как исходный класс, так и новые, добавляемые в класс функции. В классах-декораторах новые функции вызываются в требуемой последовательности — до или после вызова последующего объекта.

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

Замечания и комментарии

[править | править код]
  • Хотя объект-декоратор может добавлять свою функциональность до или после функциональности основного объекта, цепочка создаваемых объектов всегда должна заканчиваться объектом класса ConcreteComponent.
  • Базовые классы языка Java широко используют шаблон Декоратор для организации обработки операций ввода-вывода.
  • И декоратор, и адаптер являются обёртками вокруг объекта — хранят в себе ссылку на оборачиваемый объект и часто передают в него вызовы методов. Отличие декоратора от адаптера в том, что адаптер имеет внешний интерфейс, отличный от интерфейса оборачиваемого объекта, и используется именно для стыковки разных интерфейсов. Декоратор же имеет точно такой же интерфейс, и используется для добавления функциональности.
  • Для расширения функциональности класса возможно использовать как декораторы, так и стратегии. Декораторы оборачивают объект снаружи, стратегии же вставляются в него внутрь по неким интерфейсам.
    • Недостаток стратегии: класс должен быть спроектирован с возможностью вставления стратегий, декоратор же не требует такой поддержки.
    • Недостаток декоратора: он оборачивает ровно тот же интерфейс, что предназначен для внешнего мира, что вызывает смешение публичного интерфейса и интерфейса кастомизации, которое не всегда желательно.

Применение шаблона

[править | править код]

Драйверы-фильтры в ядре Windows (архитектура WDM (Windows Driver Model)) представляют собой декораторы. Несмотря на то, что WDM реализована на не-объектном языке Си, в ней чётко прослеживаются паттерны проектирования — декоратор, цепочка обязанностей, и команда (объект IRP).

Архитектура COM (Component Object Model) не поддерживает наследование реализаций, вместо него предлагается использовать декораторы (в данной архитектуре это называется «агрегация»). При этом архитектура решает (с помощью механизма pUnkOuter) проблему object identity, возникающую при использовании декораторов — identity агрегата есть identity его самого внешнего декоратора.

Ниже — пример реализации шаблона проектирования. В Python существуют декораторы функций и классов, концепция которых отличается от концепции шаблона проектирования.

Языки Delphi и Free Pascal поддерживают class helpers, которые делают ненужным использование шаблона декоратор.

Литература

[править | править код]
  • Алан Шаллоуей, Джеймс Р. Тротт. Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М.: «Вильямс», 2002. — С. 288. — ISBN 0-201-71594-5.
  • Эрик Фримен, Элизабет Фримен. Паттерны проектирования = Head First Desing Patterns. — СПб.: Питер. — 656 с. — ISBN 978-5-459-00435-9.

Примечания

[править | править код]
  1. Decorator Pattern. wiki.python.org. Дата обращения: 24 октября 2021. Архивировано 24 октября 2021 года.