Что такое паттерн проектирования Декоратор (Decorator)?

Ответ

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

Ключевой принцип: Композиция предпочтительнее наследования. Декоратор реализует тот же интерфейс, что и оборачиваемый компонент, и делегирует ему основную работу, добавляя что-то своё до или после вызова.

Участники:

  1. Компонент (Component): Общий интерфейс для декорируемых объектов и декораторов.
  2. Конкретный компонент (Concrete Component): Базовая реализация, которую мы будем декорировать.
  3. Декоратор (Decorator): Абстрактный класс, реализующий интерфейс Component и содержащий ссылку на него.
  4. Конкретный декоратор (Concrete Decorator): Добавляет конкретную функциональность.

Классический пример: Система оповещений.

// 1. Компонент - общий интерфейс
interface Notifier {
    String send(String message);
}

// 2. Конкретный компонент - базовая реализация
class SimpleNotifier implements Notifier {
    @Override
    public String send(String message) {
        return "Sending basic notification: " + message;
    }
}

// 3. Базовый декоратор (может быть абстрактным)
abstract class NotifierDecorator implements Notifier {
    protected Notifier wrappedNotifier; // Ссылка на оборачиваемый компонент

    public NotifierDecorator(Notifier notifier) {
        this.wrappedNotifier = notifier;
    }

    @Override
    public String send(String message) {
        return wrappedNotifier.send(message); // Делегирование
    }
}

// 4. Конкретные декораторы
class SMSDecorator extends NotifierDecorator {
    public SMSDecorator(Notifier notifier) {
        super(notifier);
    }

    @Override
    public String send(String message) {
        // 1. Вызываем поведение обёрнутого объекта
        String baseResult = super.send(message);
        // 2. Добавляем своё поведение
        return baseResult + "n  -> Also sending SMS: " + message;
    }
}

class FacebookDecorator extends NotifierDecorator {
    public FacebookDecorator(Notifier notifier) {
        super(notifier);
    }

    @Override
    public String send(String message) {
        return super.send(message) + "n  -> Also posting to Facebook: " + message;
    }
}

// 5. Клиентский код
public class Client {
    public static void main(String[] args) {
        Notifier notifier = new SimpleNotifier(); // База
        notifier = new SMSDecorator(notifier);    // Добавили SMS
        notifier = new FacebookDecorator(notifier); // Добавили Facebook

        System.out.println(notifier.send("Server is down!"));
    }
}
/* Вывод:
Sending basic notification: Server is down!
  -> Also sending SMS: Server is down!
  -> Also posting to Facebook: Server is down!
*/

Преимущества:

  • Гибкость: Поведение объекта можно собирать динамически во время выполнения.
  • Соблюдение OCP (Open/Closed Principle): Можно добавлять новую функциональность, не изменяя существующий код.
  • Избегание взрывного роста классов: Вместо создания классов SMSNotifier, FacebookNotifier, SMSAndFacebookNotifier и т.д. используется композиция.

Пример в Java Standard Library: Классы java.io.BufferedInputStream, java.io.DataInputStream являются декораторами для InputStream.

Ответ 18+ 🔞

А, паттерн Декоратор, ёпта! Ну это ж классика, блядь, как «Муму» Тургенева, только про код, а не про собак. Слушай сюда, я тебе сейчас на пальцах объясню, чтобы даже мартышлюшка поняла.

Представь, у тебя есть объект. Простой, как три копейки. Ну, допустим, нотификатор, который только в консоль пишет. И тут тебе начальство говорит: «А добавь-ка, дружок, ещё и смс-ки слать». Ты такой: «Окей, наследуем класс, делаем SMSNotifier». А потом: «А ещё в телегу!». Ты опять наследуешь. Потом: «А можно смс и в телегу, но без консоли?». И тут ты понимаешь, что у тебя уже овердохуища классов: SMSAndTelegramNotifier, ConsoleAndSMSNotifier, TelegramOnlyNotifier... Пиздец, блядь, терпения ноль ебать! Это ж как Герасима заставить все комбинации собак топить — нихуя не масштабируется.

Вот тут-то и выходит на сцену наш герой — Декоратор. Хитрый, как жопа с ручками. Его главная фишка — он не наследует функциональность, а оборачивает объект, как капустный лист голубцы, и добавляет сверху свой соус.

Как это работает, блядь?

  1. Компонент (Component) — это интерфейс, общий для всех. Как договор: «Все, кто хочет быть нотификатором, должны уметь send(message)». Без этого нихуя.
  2. Конкретный компонент (Concrete Component) — это наш простой, честный работяга, который делает базовую работу. SimpleNotifier, который только в консоль пишет. Немой, как Герасим, но своё дело знает.
  3. Декоратор (Decorator) — это абстрактная обёртка. Она тоже умеет send(message), но внутри у неё сидит ссылка на другой Notifier (который она оборачивает). Её метод send — это просто передача эстафеты дальше, обёрнутому объекту. Пока без своей фишки.
  4. Конкретный декоратор (Concrete Decorator) — вот где магия! Это уже обёртка с характером. Она вызывает send у того, кого оборачивает, а до или после этого делает свою, ебучую, работу. Отправить смс, запостить в фейсбук, нахуй послать — что угодно.

Смотри, как это в коде выглядит, ядрёна вошь:

// 1. Интерфейс-договор. Все должны уметь send.
interface Notifier {
    String send(String message);
}

// 2. Честный работяга. Базовая реализация.
class SimpleNotifier implements Notifier {
    @Override
    public String send(String message) {
        return "Sending basic notification: " + message; // Просто в консоль, по старинке
    }
}

// 3. Абстрактная обёртка-посредник.
abstract class NotifierDecorator implements Notifier {
    protected Notifier wrappedNotifier; // Ссылка на того, кого оборачиваем. Сердце паттерна!

    public NotifierDecorator(Notifier notifier) {
        this.wrappedNotifier = notifier; // Запоминаем, кого будем декорировать
    }

    @Override
    public String send(String message) {
        // Просто передаём вызов дальше, по цепочке
        return wrappedNotifier.send(message);
    }
}

// 4. А вот и конкретные декораторы с прибамбасами!
class SMSDecorator extends NotifierDecorator {
    public SMSDecorator(Notifier notifier) {
        super(notifier); // Говорим: "Оберни-ка вот этого чувака"
    }

    @Override
    public String send(String message) {
        // 1. Сначала дай отработать тому, кого я обёртываю
        String baseResult = super.send(message);
        // 2. А теперь моя, епта, фишка!
        return baseResult + "n  -> Also sending SMS: " + message;
    }
}

class FacebookDecorator extends NotifierDecorator {
    public FacebookDecorator(Notifier notifier) {
        super(notifier);
    }

    @Override
    public String send(String message) {
        // Можно и так: добавить своё ДО вызова обёрнутого объекта
        // System.out.println("Подготавливаю пост для Facebook...");
        return super.send(message) + "n  -> Also posting to Facebook: " + message;
    }
}

А теперь клиентский код, где мы собираем эту франкенштейн-сборку:

public class Client {
    public static void main(String[] args) {
        // Начинаем с простого нотификатора
        Notifier notifier = new SimpleNotifier(); // Просто консоль
        System.out.println("--- Только база ---");
        System.out.println(notifier.send("Пинг"));

        // Оборачиваем его в SMS-декоратор. Теперь это консоль + SMS.
        notifier = new SMSDecorator(notifier);
        System.out.println("n--- База + SMS ---");
        System.out.println(notifier.send("Пинг"));

        // Оборачиваем результат ещё раз, в Facebook-декоратор. Теперь это консоль + SMS + Facebook.
        notifier = new FacebookDecorator(notifier);
        System.out.println("n--- База + SMS + Facebook (ВОТ ЭТО ПИЗДЕЦ!) ---");
        System.out.println(notifier.send("Server is down!"));
    }
}

Вывод будет, блядь, предсказуемый и красивый:

--- Только база ---
Sending basic notification: Пинг

--- База + SMS ---
Sending basic notification: Пинг
  -> Also sending SMS: Пинг

--- База + SMS + Facebook (ВОТ ЭТО ПИЗДЕЦ!) ---
Sending basic notification: Server is down!
  -> Also sending SMS: Server is down!
  -> Also posting to Facebook: Server is down!

В чём сила, брат?

  • Гибкость, блядь, овердохуичная! Собрал комбинацию на лету, как лего. Хочешь только SMS и Facebook, но без консоли? new FacebookDecorator(new SMSDecorator(new SimpleNotifier())) — и в рот меня чих-пых, готово!
  • Принцип открыт/закрыт (OCP) в действии. Хочешь новый тип уведомления (например, в Telegram)? Создаёшь новый класс TelegramDecorator, и нихуя не ломаешь старый код. Красота!
  • Нет взрыва классов. Не нужно создавать SMSAndFacebookNotifier, SMSAndTelegramNotifier и прочую хуйню. Всё через композицию.

Где это в жизни, блядь? Да везде! Классика — java.io. BufferedInputStream — это декоратор для FileInputStream. Он оборачивает его, добавляя буферизацию. DataInputStream — ещё один декоратор, добавляющий чтение примитивов. И все они — потомки InputStream. Элегантно, сука, как танк в балете.

Короче, Декоратор — это когда нужно динамически и прозрачно для клиента накрутить объекту функциональности, не плодя ебаный зоопарк наследников. Понял? Если нет — иди, перечитай, пока не дойдёт, а то я устал, как собака, объяснять.