Ответ
Паттерн Decorator (Декоратор) — это структурный шаблон, позволяющий динамически добавлять объектам новое поведение, оборачивая их в объекты-обёртки. Это более гибкая альтернатива наследованию для расширения функциональности.
Ключевая идея: Декоратор реализует тот же интерфейс, что и исходный объект, и содержит ссылку на него. Это позволяет строить цепочки декораторов.
Пример на PHP (кофе с добавками):
// Базовый интерфейс
interface Coffee {
public function getCost(): float;
public function getDescription(): string;
}
// Конкретный компонент
class SimpleCoffee implements Coffee {
public function getCost(): float {
return 50.0;
}
public function getDescription(): string {
return "Simple coffee";
}
}
// Базовый декоратор
abstract class CoffeeDecorator implements Coffee {
protected Coffee $coffee;
public function __construct(Coffee $coffee) {
$this->coffee = $coffee;
}
public function getCost(): float {
return $this->coffee->getCost();
}
public function getDescription(): string {
return $this->coffee->getDescription();
}
}
// Конкретные декораторы
class MilkDecorator extends CoffeeDecorator {
public function getCost(): float {
return parent::getCost() + 20.0;
}
public function getDescription(): string {
return parent::getDescription() . ", milk";
}
}
class WhipDecorator extends CoffeeDecorator {
public function getCost(): float {
return parent::getCost() + 30.0;
}
public function getDescription(): string {
return parent::getDescription() . ", whip";
}
}
// Использование
$coffee = new SimpleCoffee();
echo $coffee->getDescription() . " costs " . $coffee->getCost() . "n";
// Simple coffee costs 50
$coffee = new MilkDecorator($coffee);
echo $coffee->getDescription() . " costs " . $coffee->getCost() . "n";
// Simple coffee, milk costs 70
$coffee = new WhipDecorator($coffee);
echo $coffee->getDescription() . " costs " . $coffee->getCost() . "n";
// Simple coffee, milk, whip costs 100
Преимущества:
- Гибкость: Поведение можно добавлять и комбинировать во время выполнения.
- Соответствует принципу открытости/закрытости: Классы компонентов закрыты для модификации, но открыты для расширения.
- Избегает сложных иерархий подклассов.
Недостатки:
- Может создать много маленьких классов.
- Цепочки декораторов иногда сложно конфигурировать и отлаживать.
Ответ 18+ 🔞
А, паттерн Декоратор, ёпта! Ну это ж классика, как бутерброд с колбасой, на который сверху можно навернуть сыр, икру, или, если совсем охуевший, ещё и майонезом пиздануть. Суть проще пареной репы, но мозг иногда сносит, когда начинаешь в цепочках этих обёрток разбираться.
Смотри, представь себе — есть у тебя объект, простой как три копейки. Кофе, например. И ты такой: «Хочу с молоком». Наследование — это как родить нового ребёнка специально для кофе с молоком. А если ещё и с сахаром? Рожать ещё одного. С корицей? Да ты ёбнутый, вся родословная будет из одних кофеёв. Вот тут-то Декоратор и выручает, ядрёна вошь!
Фишка в чём: ты берёшь свой объект-кофе и заворачиваешь его в другую обёртку (декоратор), которая делает вид, что она тоже кофе. А внутри себя эта обёртка просто тыкает в оригинальный объект и говорит: «Эй, чувак, дай свою цену и описание, а я сверху своё накину». И так можно оборачивать в овердохуищу слоёв, как луковицу. Получается гибко — добавил обёртку, получил новую фичу, не трогая старый код. Принцип «открыт для расширения, закрыт для изменений» в действии, ебать мои старые костыли!
Вот смотри на примере, тут всё понятно станет. Смотри код, он рабочий, я не трогал его, как велено.
// Базовый интерфейс — что умеет любой кофе?
interface Coffee {
public function getCost(): float;
public function getDescription(): string;
}
// Конкретный компонент — просто кофе, голый, без всего
class SimpleCoffee implements Coffee {
public function getCost(): float {
return 50.0;
}
public function getDescription(): string {
return "Simple coffee";
}
}
// А вот и главный трюк — базовый декоратор.
// Он тоже кофе, но внутри у него спрятан ДРУГОЙ кофе.
abstract class CoffeeDecorator implements Coffee {
protected Coffee $coffee; // Вот эта ссылка — вся магия
public function __construct(Coffee $coffee) {
$this->coffee = $coffee; // Запоминаем, что оборачиваем
}
// По умолчанию просто прокидывает вызовы внутрь
public function getCost(): float {
return $this->coffee->getCost();
}
public function getDescription(): string {
return $this->coffee->getDescription();
}
}
// Конкретные декораторы — они уже добавляют свою цену и описание
class MilkDecorator extends CoffeeDecorator {
public function getCost(): float {
// Берём стоимость того, что внутри, и добавляем своё
return parent::getCost() + 20.0;
}
public function getDescription(): string {
return parent::getDescription() . ", milk";
}
}
class WhipDecorator extends CoffeeDecorator {
public function getCost(): float {
return parent::getCost() + 30.0;
}
public function getDescription(): string {
return parent::getDescription() . ", whip";
}
}
// Использование — вот где веселье начинается!
$coffee = new SimpleCoffee();
echo $coffee->getDescription() . " costs " . $coffee->getCost() . "n";
// Выведет: Simple coffee costs 50
// Оборачиваем в молоко
$coffee = new MilkDecorator($coffee);
echo $coffee->getDescription() . " costs " . $coffee->getCost() . "n";
// Выведет: Simple coffee, milk costs 70
// Оборачиваем в сливки (взбитые, whip)
$coffee = new WhipDecorator($coffee);
echo $coffee->getDescription() . " costs " . $coffee->getCost() . "n";
// Выведет: Simple coffee, milk, whip costs 100
Плюсы, блядь, очевидные:
- Гибкость пиздец. Захотел с сиропом — создал
SyrupDecoratorи обернул. Не понравилось — убрал. Всё в рантайме, без перекомпиляций и новых родов. - Не нужно плодить сущности. Не будет у тебя класса
CoffeeWithMilkAndSugarAndCinnamonAndWhip. Вместо этого — цепочка обёрток. Красота. - Принцип открытости/закрытости в деле. Старый код
SimpleCoffeeне трогаешь, а новую функциональность добавляешь новыми классами. Чисто, опрятно.
Но и минусы есть, куда ж без них:
- Много мелких классов. Создашь
CaramelDecorator,IceDecorator,VanillaDecorator— и в проекте уже доверия ебать ноль, кто за что отвечает. Можно запутаться, как в трёх соснах. - Конфигурирование цепочек. Иногда, чтобы собрать нужный объект, нужно написать строчку кода длиннее, чем твоё терпение. Особенно если декораторов много и они зависят друг от друга. Терпения ноль ебать к концу дня.
- Отладка. Попробуй отладить цепочку из 5 обёрток, где каждая что-то меняет. Это тот ещё квест, чувак. Волнение ебать гарантировано.
Короче, инструмент мощный, но как молоток — можно гвоздь забить, а можно по пальцам получить. Используй с умом, а не просто потому, что модно.