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

Для чего нужен Adapter?

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

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

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

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

🤔 Назначение и роль паттерна Adapter

Adapter (Адаптер) — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе. Представьте себе переходник для розетки: вилка от американского устройства не подходит к европейской розетке, и адаптер решает эту проблему.

📋 Ключевая цель

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

💡 Типичные ситуации использования в PHP Backend

1. Интеграция сторонних библиотек

Когда вы подключаете внешний пакет или SDK, который имеет свой собственный способ работы, но вам нужно использовать его в рамках вашей существующей архитектуры.

// Интерфейс вашей системы
interface PaymentProcessor {
    public function process(float $amount): bool;
}

// Сторонняя библиотека с другим интерфейсом
class StripePayment {
    public function charge(int $cents): void {
        // Логика Stripe
    }
}

// Адаптер для интеграции Stripe в вашу систему
class StripeAdapter implements PaymentProcessor {
    private StripePayment $stripe;
    
    public function __construct(StripePayment $stripe) {
        $this->stripe = $stripe;
    }
    
    public function process(float $amount): bool {
        // Преобразуем доллары в центы и вызываем метод Stripe
        $cents = (int)($amount * 100);
        $this->stripe->charge($cents);
        return true;
    }
}

2. Работа с устаревшим кодом

Когда нужно модернизировать систему, но сохранить совместимость со старыми компонентами.

// Старый класс (legacy code)
class LegacyUserRepository {
    public function fetchUserData(int $id): array {
        // Возвращает данные в старом формате
        return ['user_id' => $id, 'name' => 'Old Name'];
    }
}

// Новый интерфейс
interface UserRepositoryInterface {
    public function find(int $id): User;
}

// Адаптер для старого репозитория
class LegacyUserAdapter implements UserRepositoryInterface {
    private LegacyUserRepository $legacyRepository;
    
    public function __construct(LegacyUserRepository $repository) {
        $this->legacyRepository = $repository;
    }
    
    public function find(int $id): User {
        $data = $this->legacyRepository->fetchUserData($id);
        
        // Преобразуем старый формат в новый объект
        return new User(
            id: $data['user_id'],
            name: $data['name']
        );
    }
}

3. Унификация работы с различными API

Когда система взаимодействует с несколькими внешними сервисами, имеющими разные протоколы или форматы данных.

// Адаптер для разных сервисов отправки уведомлений
class NotificationServiceAdapter {
    private $service;
    
    public function __construct($service) {
        $this->service = $service;
    }
    
    public function sendNotification(string $message, array $recipients): void {
        if ($this->service instanceof EmailService) {
            $this->service->sendEmail($recipients, $message);
        } elseif ($this->service instanceof SmsService) {
            foreach ($recipients as $phone) {
                $this->service->sendSms($phone, $message);
            }
        } elseif ($this->service instanceof SlackService) {
            $this->service->postToChannel('notifications', $message);
        }
    }
}

🎯 Преимущества использования Adapter

  • Соблюдение принципа открытости/закрытости (Open/Closed Principle): Вы можете добавлять новые адаптеры, не изменяя существующий клиентский код.
  • Упрощение тестирования: Адаптеры позволяют легко мокировать внешние зависимости в unit-тестах.
  • Снижение связности (loose coupling): Клиентский код зависит от абстракции (интерфейса), а не от конкретной реализации.
  • Постепенная миграция: Позволяет постепенно заменять старые компоненты новыми.

⚠️ Потенциальные недостатки

  • Усложнение архитектуры: Добавляет дополнительные классы в систему.
  • Накладные расходы: Небольшое снижение производительности из-за дополнительного уровня абстракции.
  • Риск создания "божественного" адаптера: Адаптер, который пытается делать слишком много, нарушая принцип единой ответственности.

🔍 Практический пример из реального проекта

В одном из моих проектов мы использовали адаптер для работы с тремя разными провайдерами геокодирования (Google Maps, Yandex Maps, OpenStreetMap). Каждый провайдер имел свой уникальный API, но благодаря адаптерам основной код работал с единым интерфейсом:

interface GeocoderInterface {
    public function geocode(string $address): Coordinates;
    public function reverseGeocode(Coordinates $coords): string;
}

// Клиентский код всегда работает единообразно
$geocoder = new GoogleMapsAdapter($googleClient);
// или
$geocoder = new YandexMapsAdapter($yandexClient);
// или
$geocoder = new OpenStreetMapAdapter($osmClient);

// Использование всегда одинаковое
$coordinates = $geocoder->geocode('Москва, Красная площадь');

📊 Когда стоит использовать Adapter?

  1. Когда нужно интегрировать класс, интерфейс которого не соответствует вашим потребностям
  2. Когда вы хотите повторно использовать существующий класс, но его интерфейс несовместим с остальной системой
  3. Когда нужно предоставить несколько вариантов интерфейса для одного класса
  4. При работе с legacy-кодом, который нельзя изменить

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