Реализовать паттерн Decorator

6. Реализовать паттерн Decorator

Условие задачи:
Необходимо реализовать паттерн проектирования Decorator.

Декоратор должен позволять динамически добавлять объекту новое поведение без изменения его исходного класса.

Для примера реализуем обработку текстового сообщения:

  • базовое сообщение возвращает исходный текст;

  • один декоратор переводит текст в верхний регистр;

  • второй добавляет в конец восклицательные знаки;

  • декораторы можно комбинировать.


Спойлеры к решению

Подсказки
💡 Создай общий интерфейс для базового объекта и декораторов.
💡 Декоратор должен хранить ссылку на объект того же интерфейса.
💡 Базовый декоратор делегирует вызов обёрнутому объекту.
💡 Конкретные декораторы добавляют поведение до или после делегирования.
💡 Декораторы можно вкладывать друг в друга.

Решение
interface Message {
    String getContent();
}

class SimpleMessage implements Message {

    private final String content;

    public SimpleMessage(String content) {
        this.content = content;
    }

    @Override
    public String getContent() {
        return content;
    }
}

abstract class MessageDecorator implements Message {

    protected final Message delegate;

    protected MessageDecorator(Message delegate) {
        this.delegate = delegate;
    }

    @Override
    public String getContent() {
        return delegate.getContent();
    }
}

class UppercaseDecorator extends MessageDecorator {

    public UppercaseDecorator(Message delegate) {
        super(delegate);
    }

    @Override
    public String getContent() {
        return super.getContent().toUpperCase();
    }
}

class ExclamationDecorator extends MessageDecorator {

    public ExclamationDecorator(Message delegate) {
        super(delegate);
    }

    @Override
    public String getContent() {
        return super.getContent() + "!!!";
    }
}

Использование:

Message message = new SimpleMessage("Hello");

message = new UppercaseDecorator(message);
message = new ExclamationDecorator(message);

System.out.println(message.getContent());

Результат:

HELLO!!!

Все компоненты реализуют один интерфейс:

interface Message {
    String getContent();
}

Поэтому декоратор можно использовать везде, где ожидается обычный Message.

Базовый декоратор хранит обёрнутый объект:

protected final Message delegate;

и делегирует ему вызов:

return delegate.getContent();

Конкретный декоратор добавляет собственное поведение. Например:

return super.getContent().toUpperCase();

Декораторы можно комбинировать:

ExclamationDecorator
UppercaseDecorator
SimpleMessage

Главное преимущество паттерна — новое поведение можно добавлять и комбинировать без изменения класса SimpleMessage и без создания большого количества его наследников.