Что такое паттерн Декоратор?

Ответ

Декоратор — это структурный паттерн проектирования, который позволяет динамически добавлять новое поведение объектам, оборачивая их в объекты-декораторы, не изменяя их исходный код.

Ключевые принципы:

  • Композиция вместо наследования: Поведение добавляется через агрегацию, что уменьшает связность по сравнению с созданием глубоких иерархий классов.
  • Принцип открытости/закрытости (Open/Closed): Классы открыты для расширения (через декораторы), но закрыты для модификации.
  • Прозрачность: Декоратор имеет тот же интерфейс, что и оборачиваемый компонент, поэтому клиентский код работает с ними одинаково.

Пример реализации на Java:

// 1. Общий интерфейс компонента
interface DataSource {
    void writeData(String data);
    String readData();
}

// 2. Конкретный компонент
class FileDataSource implements DataSource {
    private String filename;
    // ... реализация работы с файлом
    public void writeData(String data) { /* запись в файл */ }
    public String readData() { /* чтение из файла */ return ""; }
}

// 3. Базовый абстрактный декоратор
abstract class DataSourceDecorator implements DataSource {
    protected DataSource wrappee;
    public DataSourceDecorator(DataSource source) {
        this.wrappee = source;
    }
}

// 4. Конкретный декоратор для шифрования
class EncryptionDecorator extends DataSourceDecorator {
    public EncryptionDecorator(DataSource source) {
        super(source);
    }
    @Override
    public void writeData(String data) {
        String encryptedData = encrypt(data); // Логика шифрования
        wrappee.writeData(encryptedData);
    }
    @Override
    public String readData() {
        String encryptedData = wrappee.readData();
        return decrypt(encryptedData); // Логика дешифрования
    }
    private String encrypt(String data) { /* ... */ return data; }
    private String decrypt(String data) { /* ... */ return data; }
}

// 5. Использование
public class Main {
    public static void main(String[] args) {
        DataSource source = new FileDataSource("data.txt");
        // Динамически добавляем шифрование
        DataSource encryptedSource = new EncryptionDecorator(source);
        encryptedSource.writeData("Секретные данные");
    }
}

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

Ответ 18+ 🔞

Давай я тебе на пальцах объясню, что за зверь такой — декоратор. Представь, у тебя есть голая, голая функция, как будто ты сам из душа вышел. Она умеет писать и читать данные, ну, допустим, в файл. Всё, больше нихуя. А потом тебе начальник приходит и говорит: «А сделай-ка шифрование, а то данные секретные». Ты, конечно, мог бы наследоваться от этого класса и накрутить кучу наследников: EncryptedFileDataSource, CompressedFileDataSource, а потом ещё и EncryptedAndCompressedFileDataSource... Ёперный театр, это же пиздопроебибна получается, классов будет овердохуища, и каждый новый запрос — это новая иерархия, голова пухнет.

Вот тут-то декоратор и выручает, ядрёна вошь! Суть проще пареной репы. Мы не лезем в кишки исходного класса, а просто берём и оборачиваем его, как капустный лист голубцы, в другой объект. Этот объект-обёртка имеет тот же самый интерфейс, что и оригинал, но внутри он делает своё дело (например, шифрует), а потом передаёт вызов дальше, тому, кого он обернул.

Смотри на примере кода. Вот наш скромный работяга, который пишет в файл:

class FileDataSource implements DataSource {
    // ... тут он просто пишет и читает, как честный труженик
}

А теперь появляется наш декоратор — хитрая жопа, которая хочет добавить своё поведение. Он тоже реализует DataSource, но в конструкторе принимает другой DataSource, который он будет оборачивать.

class EncryptionDecorator extends DataSourceDecorator {
    @Override
    public void writeData(String data) {
        String encryptedData = encrypt(data); // 1. Сначала шифруем
        wrappee.writeData(encryptedData);    // 2. Потом отдаём обёрнутому объекту
    }
}

И вся магия в том, что ты можешь оборачивать объекты в сколько угодно обёрток, и они будут выполняться по цепочке, как матрёшки. Хочешь сжать и зашифровать? Без проблем!

DataSource source = new FileDataSource("data.txt");
source = new CompressionDecorator(source);   // Сначала сожмём
source = new EncryptionDecorator(source);    // Потом зашифруем то, что сжали
source.writeData("Секретные данные");       // И всё это пройдёт по цепочке

Получается гибко, как хуй в пальто: ты динамически, на лету, собираешь нужную функциональность. Никакого монструозного наследования, всё через композицию. Принцип «открыт для расширения, закрыт для изменения» в действии, ёпта. Используют эту штуку везде, где нужно добавить какую-то дополнительную логику — логирование, кеширование, проверку прав. В общем, паттерн — просто огонь, когда нужно накрутить поведение без головной боли.