0Pricing
PHP Academy · Урок

Структурные шаблоны: Adapter, Decorator, Facade

Объединяйте объекты и упрощайте интерфейсы с помощью структурных шаблонов

«Структурные шаблоны: Adapter, Decorator, Facade» — бесплатный урок PHP Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения PHP Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс PHP Academy содержит 4 уроков всего.

Составление объектов

Структурные паттерны описывают, как составлять классы и объекты в более крупные структуры, сохраняя их гибкость. Мы рассмотрим три основных паттерна:

  • Адаптер — делает несовместимый интерфейс пригодным для использования.
  • Декоратор — добавляет поведение без наследования.
  • Фасад — предоставляет простой интерфейс к сложной подсистеме.

Адаптер: проблема

Ваш код зависит от интерфейса PaymentGateway, но сторонний SDK предоставляет chargeCard($amountInCents) с другой формой. Прямые вызовы SDK распространяют его программный интерфейс по всему коду. Ниже два интерфейса явно несовместимы — именно такое несоответствие устраняет адаптер.

<?php
// What our app wants to depend on
interface PaymentGateway { public function pay(float $dollars): string; }

// What the vendor actually gives us (cents, array result)
final class StripeSdk {
    public function chargeCard(int $cents): array {
        return ['id' => 'ch_1', 'amount' => $cents];
    }
}
// dollars vs cents, string vs array -> shapes don't line up
var_dump((new StripeSdk())->chargeCard(2000));

Адаптер в коде

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

<?php
interface PaymentGateway { public function pay(float $dollars): string; }

// Third-party SDK with an incompatible API
final class StripeSdk {
    public function chargeCard(int $cents): array {
        return ['id' => 'ch_1', 'amount' => $cents];
    }
}

final class StripeAdapter implements PaymentGateway {
    public function __construct(private StripeSdk $sdk) {}
    public function pay(float $dollars): string {
        $res = $this->sdk->chargeCard((int) round($dollars * 100));
        return "charged {$res['amount']}c -> {$res['id']}";
    }
}
echo (new StripeAdapter(new StripeSdk()))->pay(20.0), PHP_EOL;

Идея декоратора

Декоратор оборачивает объект с тем же интерфейсом и добавляет поведение до или после делегирования вызова. В отличие от наследования, декораторы объединяются во время выполнения, позволяя сочетать возможности в любом порядке без взрывного роста числа классов. Ниже приведён минимальный каркас: оболочка реализует тот же интерфейс и хранит внутренний экземпляр.

<?php
interface Coffee { public function cost(): float; }
final class Espresso implements Coffee {
    public function cost(): float { return 2.0; }
}
final class WithMilk implements Coffee {
    public function __construct(private Coffee $inner) {}
    public function cost(): float { return $this->inner->cost() + 0.5; }
}
echo (new WithMilk(new Espresso()))->cost(), PHP_EOL; // 2.5

Декоратор в коде

Каждый декоратор реализует тот же интерфейс и хранит внутренний экземпляр. Здесь источник данных оборачивается кэшированием и журналированием, которые можно свободно сочетать.

<?php
interface DataSource { public function read(): string; }

final class FileSource implements DataSource {
    public function read(): string { return 'raw-data'; }
}

final class UpperCaseDecorator implements DataSource {
    public function __construct(private DataSource $inner) {}
    public function read(): string { return strtoupper($this->inner->read()); }
}
final class ExclaimDecorator implements DataSource {
    public function __construct(private DataSource $inner) {}
    public function read(): string { return $this->inner->read() . '!!!'; }
}

$src = new ExclaimDecorator(new UpperCaseDecorator(new FileSource()));
echo $src->read(), PHP_EOL; // RAW-DATA!!!

Декораторы в реальных системах

Промежуточные обработчики PSR-7 и PSR-15 по сути представляют собой паттерн декоратора, применённый к обработке HTTP: каждый обработчик оборачивает следующий, добавляя проверку подлинности, журналирование или кэширование вокруг запроса и ответа. Обёртки потоков и уровни кэширования PSR-6 и PSR-16 также часто используют декорирование. Ключевое свойство: обёртку можно подставить вместо объекта, который она оборачивает.

Декоратор и наследование

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

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

Идея фасада

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

<?php
// Without a facade, the client wires every step itself
final class Cpu { public function boot(): string { return 'cpu '; } }
final class Disk { public function load(): string { return 'disk '; } }
final class Ram { public function check(): string { return 'ram'; } }

$out = (new Cpu())->boot() . (new Disk())->load() . (new Ram())->check();
echo $out, PHP_EOL; // cpu disk ram

Фасад в коде

Фасад координирует работу нескольких взаимодействующих объектов за одним методом. Клиенты вызывают place(), вместо того чтобы самостоятельно связывать запасы, оплату и доставку.

<?php
final class Inventory { public function reserve(string $sku): bool { return true; } }
final class Payments { public function charge(float $amt): bool { return true; } }
final class Shipping { public function dispatch(string $sku): string { return 'tracking-99'; } }

final class OrderFacade {
    public function __construct(
        private Inventory $inv,
        private Payments $pay,
        private Shipping $ship
    ) {}
    public function place(string $sku, float $amount): string {
        if (!$this->inv->reserve($sku)) { return 'out of stock'; }
        if (!$this->pay->charge($amount)) { return 'payment failed'; }
        return 'shipped: ' . $this->ship->dispatch($sku);
    }
}
echo (new OrderFacade(new Inventory(), new Payments(), new Shipping()))
    ->place('ABC', 49.99), PHP_EOL;

Различия между тремя паттернами

  • Адаптер изменяет интерфейс, чтобы две существующие части могли взаимодействовать: поведение то же, форма другая.
  • Декоратор сохраняет интерфейс, но добавляет поведение, которое можно объединять во время выполнения.
  • Фасад вводит новый, более простой интерфейс перед множеством классов.

Полезная проверка: адаптер отвечает за совместимость, декоратор — за расширение, а фасад — за упрощение.

О фасадах Laravel

«Фасады» Laravel — это механизм статического прокси, работающий на основе контейнера служб, а не паттерн «Фасад». Их объединяет цель предоставить удобную точку входа, но реализация различается. Рассуждая о проектировании, не смешивайте терминологию фреймворка со структурным паттерном.

Быстрая проверка

Определите паттерн по его назначению.

Итоги

Вы составляли объекты с помощью трёх структурных паттернов: адаптера для соединения несовместимых интерфейсов, например интерфейса поставщика SDK, декоратора для объединения поведения во время выполнения, лежащего в основе промежуточных обработчиков PSR-15, и фасада для создания простого интерфейса перед сложной подсистемой. Каждый паттерн уменьшает связанность, помогая коду противостоять зависимости от одного поставщика и разрастанию функциональности.

Часто задаваемые вопросы

Урок «Структурные шаблоны: Adapter, Decorator, Facade» бесплатный?

Да — полный текст урока «Структурные шаблоны: Adapter, Decorator, Facade» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс PHP Academy, подпишись на CoddyKit PRO. Курс PHP Academy содержит 4 уроков всего.

Чему я научусь в уроке «Структурные шаблоны: Adapter, Decorator, Facade»?

Объединяйте объекты и упрощайте интерфейсы с помощью структурных шаблонов Ты практикуешь PHP Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать PHP Academy?

Предыдущий опыт не требуется. PHP Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Структурные шаблоны: Adapter, Decorator, Facade»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке PHP Academy?

Да. Каждый урок PHP Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Принципы SOLID на практике
  2. Порождающие шаблоны: Factory, Builder, Singleton
  3. Структурные шаблоны: Adapter, Decorator, Facade
  4. Поведенческие шаблоны: Strategy, Observer, Command
← Назад к PHP Academy