← Назад к вопросам

Что такое шаблон проектирования Adapter?

2.0 Middle🔥 121 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Шаблон проектирования Adapter (Адаптер)

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

Основная цель и проблематика

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

Типы реализаций Adapter

  1. Адаптер класса (Class Adapter). Использует наследование: адаптер наследует оба интерфейса (и целевой, и адаптируемый). В PHP это реализуется через наследование одного класса и реализацию интерфейса.

    // Целевой интерфейс, который ожидает клиент
    interface Notification {
        public function send(string $title, string $message): void;
    }
    
    // Адаптируемый класс (старая библиотека, которую нельзя изменить)
    class SlackNotification {
        public function sendToChannel(string $chatId, string $text): void {
            echo "Slack: отправлено в канал '$chatId' текст '$text'\n";
        }
    }
    
    // Адаптер КЛАССА (использует наследование)
    class SlackNotificationAdapter extends SlackNotification implements Notification {
        private string $chatId;
    
        public function __construct(string $chatId) {
            $this->chatId = $chatId;
        }
    
        public function send(string $title, string $message): void {
            $text = "[$title] $message";
            // Адаптер вызывает унаследованный метод родителя с нужными параметрами
            $this->sendToChannel($this->chatId, $text);
        }
    }
    
  2. Адаптер объекта (Object Adapter). Использует композицию: адаптер содержит экземпляр адаптируемого класса (объект) и делегирует ему вызовы. Это более гибкий и рекомендуемый подход, соответствующий принципу "предпочитайте композицию наследованию".

    // Тот же целевой интерфейс Notification...
    
    // Адаптер ОБЪЕКТА (использует композицию)
    class SlackNotificationAdapter implements Notification {
        private SlackNotification $slackNotification;
        private string $chatId;
    
        // В конструктор передаём экземпляр адаптируемого объекта
        public function __construct(SlackNotification $slackNotification, string $chatId) {
            $this->slackNotification = $slackNotification;
            $this->chatId = $chatId;
        }
    
        public function send(string $title, string $message): void {
            $text = "[$title] $message";
            // Адаптер делегирует вызов объекту SlackNotification
            $this->slackNotification->sendToChannel($this->chatId, $text);
        }
    }
    
    // Клиентский код, работающий через целевой интерфейс
    function clientCode(Notification $notification) {
        $notification->send("Важное уведомление", "Сервер будет перезагружен в 03:00");
    }
    
    // Использование
    $slack = new SlackNotification();
    $adapter = new SlackNotificationAdapter($slack, "general");
    clientCode($adapter); // Выведет: Slack: отправлено в канал 'general' текст '[Важное уведомление] Сервер будет перезагружен в 03:00'
    

Когда применять Adapter?

  • Интеграция сторонних библиотек или устаревшего кода (legacy code). Когда нужно подключить класс с неподходящим интерфейсом, и вы не можете (или не хотите) менять его исходный код.
  • Работа с несколькими классами, имеющими схожую функциональность, но разные интерфейсы. Адаптер позволяет унифицировать доступ к ним через общий интерфейс, упрощая клиентский код.
  • Создание повторно используемых классов. Класс может быть полезен, но его интерфейс слишком специфичен. Адаптер делает его пригодным для использования в других контекстах.

Преимущества и недостатки

Преимущества:

  • Принцип единой ответственности (Single Responsibility Principle): Можно отделить код преобразования интерфейса от основной бизнес-логики.
  • Принцип открытости/закрытости (Open/Closed Principle): Можно вводить новые адаптеры, не ломая существующий клиентский код.
  • Обеспечивает обратную совместимость, что критически важно при постепенной модернизации системы.
  • Упрощает тестирование: Можно создавать mock-адаптеры для тестирования клиентского кода.

Недостатки:

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

Пример из реального мира в PHP

Классический пример — адаптация различных драйверов баз данных или кэшей под единый интерфейс. Например, фреймворк может работать с разными СУБД (MySQL, PostgreSQL, SQLite) через единый интерфейс Connection. Каждому драйверу соответствует свой адаптер, который преобразует вызовы методов интерфейса Connection в специфичные для данной базы данных запросы.

Итог: Паттерн Adapter — это мощный и элегантный способ обеспечения взаимодействия между компонентами, которые изначально не были предназначены для совместной работы. Он широко применяется в PHP-экосистеме, особенно при интеграции пакетов из Composer, работе с различными API и поддержке legacy-кода в современных приложениях.