Паттерн Адаптер в PHP
Короткий скринкаст: паттерн «Адаптер» (Adapter) — приводим чуждое API к единому контракту, не трогая легаси-код.
Описание
У клиента есть контракт PaymentInterface::process(). Старый шлюз LegacyPaymentGateway не совместим с ним: у него charge() и возвращает bool. Адаптер PaymentAdapter реализует контракт, держит внутри легаси-шлюз и переводит его вызовы в ожидаемый ответ. Легаси-код не меняется.
UML-диаграмма
Код
PaymentInterface.php
interface PaymentInterface
{
public function process(float $amount): string;
}
LegacyPaymentGateway.php
class LegacyPaymentGateway
{
public function charge(float $amount): bool
{
return $amount > 0;
}
}
PaymentAdapter.php
class PaymentAdapter implements PaymentInterface
{
public function __construct(
private LegacyPaymentGateway $gateway
) {}
public function process(float $amount): string
{
$ok = $this->gateway->charge($amount);
return $ok ? "paid {$amount}" : 'declined';
}
}
demo.php
require_once 'PaymentInterface.php';
require_once 'LegacyPaymentGateway.php';
require_once 'PaymentAdapter.php';
function checkout(PaymentInterface $payment, float $amount): void
{
echo $payment->process($amount) . '!';
}
$gateway = new LegacyPaymentGateway();
$payment = new PaymentAdapter($gateway);
checkout($payment, 99.5);
echo PHP_EOL;
checkout($payment, 0);
Заметка: что даёт адаптер
Адаптер — это единственный «шов», через который легаси или сторонний SDK работает с нашим кодом. Клиент зависят от интерфейса, а не от конкретной реализации: заменить шлюз или добавить новую реализацию легко. Главный принцип — чужой код не правим руками, его оборачиваем.
Пример результата
После php demo.php в терминале появится:
paid 99.5!
declined!
Преимущества
- Позволяет переиспользовать несовместимый код без его правок.
- Изолирует клиента от сторонней библиотеки или legacy-API.
- Всё преобразование собрано в одном месте — в адаптере.
- Позволяет подменять одну реализацию другой за общим интерфейсом.
Когда использовать
- Когда нужно подключить стороннюю или legacy-библиотеку с чужим интерфейсом.
- Когда интерфейсы клиента и подсистемы не совпадают.
- Когда важно изолировать внешнюю зависимость в одном классе.
- Когда нужно несколько реализаций под одним общим интерфейсом.
Использование в Laravel
В Laravel Адаптер — это Illuminate\Filesystem\FilesystemAdapter: он приводит разные драйверы Flysystem (local, s3, ftp) к единому интерфейсу Illuminate\Contracts\Filesystem\Filesystem с методами put(), get(), delete(). Так же устроены адаптеры кэша (Illuminate\Cache\RedisStore), почтовые транспорты и коннекторы очередей: внешняя библиотека спрятана за контрактом Laravel.