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

«Что такое паттерн проектирования Декоратор (Decorator)?» — вопрос из категории Паттерны, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Паттерн Декоратор (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.