Паттерн Decorator (Декоратор)
Паттерн Decorator (Декоратор) — это структурный паттерн проектирования, который позволяет динамически добавлять объектам новую функциональность, оборачивая их в полезные "обёртки". Это гибкая альтернатива наследованию для расширения функциональности.
Основные концепции Decorator
- Динамическое добавление ответственности: Позволяет добавлять новое поведение объектам без изменения их класса
- Альтернатива наследованию: В отличие от наследования, декораторы обеспечивают более гибкое расширение функциональности
- Композиция вместо наследования: Использует композицию для объединения поведения во время выполнения
Структура паттерна
- Компонент (Component): Интерфейс для объектов, которые могут иметь добавленные обязанности
- Конкретный компонент (ConcreteComponent): Конкретный объект, который может быть обёрнут декораторами
- Декоратор (Decorator): Хранит ссылку на компонент и реализует его интерфейс
- Конкретные декораторы (ConcreteDecorators): Добавляют конкретную функциональность
Преимущества
- Большая гибкость, чем у наследования
- Позволяет избежать перегруженных функциями классов на верхних уровнях иерархии
- Можно добавлять и удалять обязанности во время выполнения
- Можно комбинировать несколько декораторов
Недостатки
- Может привести к большому количеству маленьких классов
- Трудно реализовать декоратор, который не зависит от порядка других декораторов
- Исходный код может быть сложнее для понимания из-за множества обёрток
Примеры реализации на Python
1. Базовый пример с текстом
from abc import ABC, abstractmethod
# Интерфейс компонента
class TextComponent(ABC):
@abstractmethod
def render(self) -> str:
pass
# Конкретный компонент
class PlainText(TextComponent):
def __init__(self, text):
self._text = text
def render(self) -> str:
return self._text
# Базовый декоратор
class TextDecorator(TextComponent):
def __init__(self, component: TextComponent):
self._component = component
def render(self) -> str:
return self._component.render()
# Конкретные декораторы
class BoldDecorator(TextDecorator):
def render(self) -> str:
return f"<b>{self._component.render()}</b>"
class ItalicDecorator(TextDecorator):
def render(self) -> str:
return f"<i>{self._component.render()}</i>"
class UnderlineDecorator(TextDecorator):
def render(self) -> str:
return f"<u>{self._component.render()}</u>"
# Использование
text = PlainText("Hello, World!")
decorated_text = BoldDecorator(ItalicDecorator(UnderlineDecorator(text)))
print(decorated_text.render()) # <b><i><u>Hello, World!</u></i></b>
2. Пример с кофе и добавками
from abc import ABC, abstractmethod
# Абстрактный компонент - напиток
class Beverage(ABC):
@abstractmethod
def get_description(self) -> str:
pass
@abstractmethod
def cost(self) -> float:
pass
# Конкретные компоненты - виды кофе
class Espresso(Beverage):
def get_description(self) -> str:
return "Espresso"
def cost(self) -> float:
return 1.99
class DarkRoast(Beverage):
def get_description(self) -> str:
return "Dark Roast Coffee"
def cost(self) -> float:
return 0.99
# Абстрактный декоратор - добавка
class CondimentDecorator(Beverage, ABC):
def __init__(self, beverage: Beverage):
self._beverage = beverage
@abstractmethod
def get_description(self) -> str:
pass
# Конкретные декораторы - виды добавок
class Milk(CondimentDecorator):
def get_description(self) -> str:
return self._beverage.get_description() + ", Milk"
def cost(self) -> float:
return self._beverage.cost() + 0.20
class Mocha(CondimentDecorator):
def get_description(self) -> str:
return self._beverage.get_description() + ", Mocha"
def cost(self) -> float:
return self._beverage.cost() + 0.30
class Whip(CondimentDecorator):
def get_description(self) -> str:
return self._beverage.get_description() + ", Whip"
def cost(self) -> float:
return self._beverage.cost() + 0.15
# Использование
beverage = Espresso()
print(f"{beverage.get_description()} ${beverage.cost()}")
beverage2 = DarkRoast()
beverage2 = Mocha(beverage2)
beverage2 = Mocha(beverage2)
beverage2 = Whip(beverage2)
print(f"{beverage2.get_description()} ${beverage2.cost()}")
3. Пример с декораторами функций (встроенная поддержка в Python)
Python имеет встроенную поддержку декораторов для функций:
def make_bold(func):
def wrapper(*args, **kwargs):
return f"<b>{func(*args, **kwargs)}</b>"
return wrapper
def make_italic(func):
def wrapper(*args, **kwargs):
return f"<i>{func(*args, **kwargs)}</i>"
return wrapper
def make_underline(func):
def wrapper(*args, **kwargs):
return f"<u>{func(*args, **kwargs)}</u>"
return wrapper
# Применение нескольких декораторов
@make_bold
@make_italic
@make_underline
def hello(name):
return f"Hello, {name}!"
print(hello("World")) # <b><i><u>Hello, World!</u></i></b>
# Эквивалентно:
# hello = make_bold(make_italic(make_underline(hello)))
Когда использовать Decorator?
- Когда нужно добавлять обязанности объектам динамически и прозрачно
- Когда нельзя расширить функциональность с помощью наследования
- Когда нужно добавлять и удалять обязанности во время выполнения
Decorator особенно полезен в системах, где важно соблюдение принципа открытости/закрытости (классы должны быть открыты для расширения, но закрыты для модификации).