Какую проблему решает декоратор

Ответ

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

Пример с кэшированием:

interface DataProvider {
    public function fetchData(): array;
}

class DbDataProvider implements DataProvider {
    public function fetchData(): array {
        return ['data' => 'from db'];
    }
}

class CacheDecorator implements DataProvider {
    private $provider;
    private $cache;

    public function __construct(DataProvider $provider) {
        $this->provider = $provider;
    }

    public function fetchData(): array {
        if ($this->cache) return $this->cache;

        $this->cache = $this->provider->fetchData();
        return $this->cache;
    }
}

// Использование
$provider = new CacheDecorator(new DbDataProvider());
$data = $provider->fetchData(); // Будет закешировано

Плюсы:

  • Гибкость: можно комбинировать декораторы
  • Открытость/закрытость: не изменяем исходный класс
  • Чистая композиция вместо наследования

Ответ 18+ 🔞

А, декоратор, ёпта! Это ж как на новогоднюю ёлку гирлянды вешать, только для кода. Суть в чём, чувак? Ну вот представь, у тебя есть объект, простой, как пять копеек. А тебе вдруг надо, чтобы он ещё и кэшировал, и логировал, и по сети данные шифровал перед отправкой. И что, на каждый чих новый класс плодить? Ёб твою мать, так до завтра кодить будешь, а потом ещё и поддерживать эту манду с ушами.

Вот тут декоратор и выручает. Берёшь свой базовый объект, и просто, как шубу, оборачиваешь его в другой, который добавляет одну новую фишку. А потом этот обёрнутый — в следующий, который добавляет ещё одну. Получается такая матрёшка, хитрая жопа, но чертовски удобная.

Смотри на примере, тут всё понятно становится. Вот есть интерфейс DataProvider. Просто говорит: «Умею данные отдавать, да похуй откуда».

interface DataProvider {
    public function fetchData(): array;
}

Дальше идёт честный работяга, который тащит данные прямиком из базы. Без фокусов.

class DbDataProvider implements DataProvider {
    public function fetchData(): array {
        return ['data' => 'from db'];
    }
}

И вот появляется наш герой — CacheDecorator. Это он и есть, тот самый декоратор, полупидор в хорошем смысле. Его задача — добавить кэширование ЛЮБОМУ провайдеру, который засунешь ему в конструктор.

class CacheDecorator implements DataProvider {
    private $provider;
    private $cache;

    public function __construct(DataProvider $provider) {
        $this->provider = $provider; // Запоминаем, кого оборачиваем
    }

    public function fetchData(): array {
        // Если в кэше уже есть — нахуй не паримся, отдаём оттуда
        if ($this->cache) return $this->cache;

        // Если нет — идём к оригинальному провайдеру, берём у него
        $this->cache = $this->provider->fetchData();
        return $this->cache;
    }
}

А использование — это просто песня. Хочешь кэширующего провайдера? Без проблем, оборачивай.

$provider = new CacheDecorator(new DbDataProvider());
$data = $provider->fetchData(); // Первый раз сходит в базу, второй — уже из кэша

А завтра понадобится ещё и логирование? Да хуй с горы! Сделаешь LoggingDecorator и обернёшь им всё то же самое. Получится new LoggingDecorator(new CacheDecorator(new DbDataProvider())). Каждый слой делает своё дело, не лезет в чужое, и все довольны.

Пиздец какие плюсы:

  • Гибкость, блядь, овердохуища: Собираешь функциональность, как из Лего. Нужен кэш, но без логирования? Убрал один слой — и готово. Волнение ебать от такой простоты.
  • Принцип открытости/закрытости: Исходный класс DbDataProvider ты не трогаешь вообще. Он как был святым, так и остался. Новую функциональность добавляешь снаружи, обёртками. Доверия ебать ноль к тем, кто лезет в рабочий код с кривыми правками.
  • Композиция вместо наследования: Не нужно создавать класс DbDataProviderWithCacheAndLogging, который унаследует всё от всех и превратится в пидараса шерстяного, которого и тронуть страшно. Всё чисто, всё на совести.

В общем, паттерн — ядрёна вошь. Когда нужно на лету прикрутить фичи к объекту, не разбирая его на запчасти, он выручает по полной. Главное — не увлечься и не сделать обёрток на двадцать слоёв, а то отладка превратится в кромешный ад.