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

Что такое паттерн Адаптер (Adapter)?

2.0 Middle🔥 121 комментариев
#SOLID и паттерны проектирования

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

# Паттерн Адаптер (Adapter Pattern)

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

Основная проблема

Представьте ситуацию:

  • У вас есть готовый класс PayPalPaymentService с методом payWithPayPal(amount)
  • Ваше приложение ожидает интерфейс PaymentProcessor с методом process(amount)
  • Вы не можете изменить ни один из них

Решение: создать адаптер, который переводит один интерфейс в другой.

Структурные компоненты

Client → Target Interface ← Adapter ← Adaptee (несовместимый класс)
  • Client — клиентский код
  • Target Interface — интерфейс, который ожидает клиент
  • Adapter — переходник между интерфейсами
  • Adaptee — существующий класс с несовместимым интерфейсом

Два типа Адаптера

1. Адаптер на основе наследования (Class Adapter)

// Несовместимый интерфейс (Adaptee)
class OldPaymentSystem {
    public String makePayment(double amount) {
        return "Платеж на сумму " + amount + " рублей обработан";
    }
}

// Целевой интерфейс (Target)
interface NewPaymentProcessor {
    void processPayment(double amount);
}

// Адаптер (наследование)
class PaymentAdapter extends OldPaymentSystem implements NewPaymentProcessor {
    @Override
    public void processPayment(double amount) {
        String result = makePayment(amount); // Используем старый метод
        System.out.println(result);
    }
}

// Использование
public class Main {
    public static void main(String[] args) {
        NewPaymentProcessor processor = new PaymentAdapter();
        processor.processPayment(1000.0); // Работает!
    }
}

2. Адаптер на основе композиции (Object Adapter) — рекомендуется

// Несовместимый интерфейс (Adaptee)
class PayPalService {
    public void sendPayment(double amount, String email) {
        System.out.println("PayPal платеж: " + amount + " на " + email);
    }
}

// Целевой интерфейс (Target)
interface PaymentGateway {
    void pay(double amount);
}

// Адаптер (композиция)
class PayPalAdapter implements PaymentGateway {
    private PayPalService payPalService;
    private String userEmail;
    
    public PayPalAdapter(PayPalService service, String email) {
        this.payPalService = service;
        this.userEmail = email;
    }
    
    @Override
    public void pay(double amount) {
        payPalService.sendPayment(amount, userEmail);
    }
}

// Использование
public class CheckoutService {
    private PaymentGateway gateway;
    
    public CheckoutService(PaymentGateway gateway) {
        this.gateway = gateway;
    }
    
    public void checkout(double amount) {
        gateway.pay(amount);
    }
    
    public static void main(String[] args) {
        PayPalService paypal = new PayPalService();
        PaymentGateway adapter = new PayPalAdapter(paypal, "user@example.com");
        CheckoutService checkout = new CheckoutService(adapter);
        checkout.checkout(500.0); // Работает!
    }
}

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

Пример 1: Интеграция различных логирующих систем

// Старая система логирования (Adaptee)
class LegacyLogger {
    public void writeLog(String message, int level) {
        System.out.println("[LEVEL " + level + "] " + message);
    }
}

// Новый интерфейс (Target)
interface ModernLogger {
    void info(String message);
    void error(String message);
    void debug(String message);
}

// Адаптер
class LoggerAdapter implements ModernLogger {
    private LegacyLogger legacyLogger;
    
    public LoggerAdapter(LegacyLogger logger) {
        this.legacyLogger = logger;
    }
    
    @Override
    public void info(String message) {
        legacyLogger.writeLog(message, 1); // Info = level 1
    }
    
    @Override
    public void error(String message) {
        legacyLogger.writeLog(message, 3); // Error = level 3
    }
    
    @Override
    public void debug(String message) {
        legacyLogger.writeLog(message, 0); // Debug = level 0
    }
}

Пример 2: Работа с различными БД

// Несовместимые интерфейсы
class MySQLDatabase {
    public ResultSet executeQuery(String sql) {
        System.out.println("Выполнение MySQL запроса: " + sql);
        return null;
    }
}

class MongoDBDatabase {
    public Document findOne(String collection, Map<String, Object> filter) {
        System.out.println("Поиск в MongoDB коллекции: " + collection);
        return null;
    }
}

// Единый интерфейс
interface Database {
    Object executeQuery(String query);
}

// Адаптеры
class MySQLAdapter implements Database {
    private MySQLDatabase mysql;
    
    public MySQLAdapter(MySQLDatabase db) {
        this.mysql = db;
    }
    
    @Override
    public Object executeQuery(String query) {
        return mysql.executeQuery(query);
    }
}

class MongoDBAdapter implements Database {
    private MongoDBDatabase mongodb;
    
    public MongoDBAdapter(MongoDBDatabase db) {
        this.mongodb = db;
    }
    
    @Override
    public Object executeQuery(String query) {
        // Парсим query и преобразуем в MongoDB операцию
        return mongodb.findOne("users", new HashMap<>());
    }
}

// Использование
class DataRepository {
    private Database database;
    
    public DataRepository(Database db) {
        this.database = db;
    }
    
    public Object getUsers() {
        return database.executeQuery("SELECT * FROM users");
    }
}

Пример 3: Адаптация коллекций

Java использует Adapter паттерн внутри:

// Arrays.asList() - адаптирует array в List
String[] array = {"a", "b", "c"};
List<String> list = Arrays.asList(array); // Адаптер!

// Collections.list() - адаптирует Enumeration в List
Enumeration<String> enumeration = Collections.enumeration(list);
List<String> adapted = Collections.list(enumeration); // Адаптер!

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

Используй, когда:

  • Нужно использовать класс с несовместимым интерфейсом
  • Хочешь обеспечить совместимость между несколькими системами
  • Нужно переиспользовать старый код в новой архитектуре
  • Интегрируешь сторонние библиотеки

Не используй, если:

  • Можно изменить интерфейс оригинального класса
  • Паттерн усложняет код без необходимости
  • Лучше подходит другой паттерн (Facade, Proxy)

Адаптер vs другие паттерны

ПаттернНазначениеКогда использовать
AdapterПереводит интерфейсыНесовместимые интерфейсы
FacadeУпрощает интерфейсСложная подсистема
ProxyКонтролирует доступНужна ленивая загрузка, проверка
DecoratorДобавляет функциональностьДинамическое расширение

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

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

  • Позволяет использовать несовместимый код
  • Разделяет интерфейс клиента от реализации
  • Улучшает переиспользуемость кода
  • Изолирует от изменений внешних систем

Недостатки:

  • Добавляет дополнительные классы
  • Может усложнить отладку
  • Может повлиять на производительность

Выводы: Adapter — это мощный паттерн для интеграции несовместимых систем. Он особенно полезен при работе с наследственным кодом и сторонними библиотеками. Выбирай композицию вместо наследования (Object Adapter) для большей гибкости.

Что такое паттерн Адаптер (Adapter)? | PrepBro