Какую проблему решает паттерн «Адаптер»

Ответ

Паттерн "Адаптер" позволяет работать с несовместимыми интерфейсами, преобразуя один интерфейс класса в другой, ожидаемый клиентом. Это полезно при интеграции сторонних библиотек или устаревшего кода без изменения их исходного кода.

Пример:

interface PaymentGateway {
    public function pay(float $amount): void;
}

class LegacyPaymentSystem {
    public function makePayment(float $amount, string $currency): void {
        echo "Paying $amount $currency via legacy systemn";
    }
}

class PaymentAdapter implements PaymentGateway {
    private LegacyPaymentSystem $legacySystem;

    public function __construct(LegacyPaymentSystem $legacySystem) {
        $this->legacySystem = $legacySystem;
    }

    public function pay(float $amount): void {
        $this->legacySystem->makePayment($amount, 'USD');
    }
}

// Клиентский код работает с PaymentGateway
$gateway = new PaymentAdapter(new LegacyPaymentSystem());
$gateway->pay(100.50);

Адаптер преобразует вызов pay() в makePayment(), скрывая различия в интерфейсах.

Ответ 18+ 🔞

А, паттерн "Адаптер"! Ну это же классика, ёпта. Представь себе ситуацию: у тебя есть новый, красивый, блестящий код, который знает, как работать только с одним типом розетки. А тут подсовывают какой-то легаси-компонент — старый, видавший виды, с разъёмами, как у древнего системного блока. И он тупо не втыкается, пизда рулю.

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

Смотри, вот пример, чтобы совсем понятно было. Допустим, у тебя в проекте везде используется модный интерфейс PaymentGateway с методом pay(). Всё красиво, всё единообразно.

interface PaymentGateway {
    public function pay(float $amount): void;
}

А потом бац — и тебе нужно прикрутить платежку из каменного века, LegacyPaymentSystem. И этот уродец принимает не только сумму, но ещё и валюту отдельным параметром. Совсем другой протокол, блядь.

class LegacyPaymentSystem {
    public function makePayment(float $amount, string $currency): void {
        echo "Paying $amount $currency via legacy systemn";
    }
}

И что, переписывать под него всю систему? Да хуй там! Напрямую его использовать не получится — интерфейсы не совпадают, будет тебе ошибка на ровном месте.

Вот тут и вырубает адаптер, ядрёна вошь. Создаёшь класс-посредник, который реализует твой новый интерфейс, но внутри содержит старый объект и переводит вызовы.

class PaymentAdapter implements PaymentGateway {
    private LegacyPaymentSystem $legacySystem;

    public function __construct(LegacyPaymentSystem $legacySystem) {
        $this->legacySystem = $legacySystem;
    }

    public function pay(float $amount): void {
        // Вся магия здесь! Говорим старой системе то, что она хочет услышать.
        $this->legacySystem->makePayment($amount, 'USD'); // Подставляем валюту по умолчанию
    }
}

И теперь твой клиентский код может спокойно работать, даже не подозревая, что внутри творится какая-то алхимия. Для него это просто ещё один PaymentGateway.

// Клиенту похуй, что внутри адаптер. Он просто платит.
$gateway = new PaymentAdapter(new LegacyPaymentSystem());
$gateway->pay(100.50);

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