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

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

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

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

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

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

# Паттерн Adapter (Адаптер): структурный паттерн проектирования

Определение

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

Аналогия из реальной жизни: электрический адаптер позволяет подключить европейскую вилку к американской розетке. Вилка и розетка несовместимы, но адаптер решает эту проблему.

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

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

  • Нужно интегрировать сторонний код, интерфейс которого несовместим с твоим
  • Есть наследование от класса с неподходящим интерфейсом
  • Хочешь использовать старый класс с новой функциональностью
  • Нужно объединить несколько несовместимых интерфейсов

Типы Adapter

1. Class Adapter (использует наследование)

# Несовместимые интерфейсы
class OldPaymentGateway:
    """Старый платёжный шлюз"""
    def process_payment(self, amount):
        print(f"Старый шлюз: обработка {amount}")
        return True

class NewPaymentProcessor:
    """Новый процессор, ожидающий другой интерфейс"""
    def execute_transaction(self, amount, currency='USD'):
        raise NotImplementedError

# Adapter, преобразующий интерфейсы
class OldToNewPaymentAdapter(NewPaymentProcessor):
    """Адаптер для работы со старым шлюзом через новый интерфейс"""
    
    def __init__(self, old_gateway):
        self.old_gateway = old_gateway
    
    def execute_transaction(self, amount, currency='USD'):
        # Преобразуем новый интерфейс в старый
        print(f"Адаптер: конвертируем {amount} {currency}")
        return self.old_gateway.process_payment(amount)

# Использование
old_gateway = OldPaymentGateway()
adapter = OldToNewPaymentAdapter(old_gateway)
result = adapter.execute_transaction(100, 'EUR')

2. Object Adapter (использует композицию) — ПРЕДПОЧТИТЕЛЕН

from abc import ABC, abstractmethod

class PaymentProcessor(ABC):
    """Ожидаемый интерфейс"""
    @abstractmethod
    def pay(self, amount: float) -> bool:
        pass

class LegacyStripeAPI:
    """Старый API, несовместимый интерфейс"""
    def charge_card(self, cents: int) -> bool:
        print(f"Stripe: списано {cents} центов")
        return True

class StripeAdapter(PaymentProcessor):
    """Адаптер для Stripe"""
    
    def __init__(self, stripe_api: LegacyStripeAPI):
        self.stripe_api = stripe_api  # композиция
    
    def pay(self, amount: float) -> bool:
        # Преобразуем доллары в центы
        cents = int(amount * 100)
        return self.stripe_api.charge_card(cents)

# Использование
stripe_api = LegacyStripeAPI()
processor = StripeAdapter(stripe_api)
processor.pay(29.99)  # доллары → центы

Практический пример: интеграция разных DB

from abc import ABC, abstractmethod

# Ожидаемый интерфейс нашего приложения
class Database(ABC):
    @abstractmethod
    def connect(self):
        pass
    
    @abstractmethod
    def execute_query(self, query: str) -> list:
        pass
    
    @abstractmethod
    def close(self):
        pass

# Старый MySQL драйвер с другим интерфейсом
class MySQLOldDriver:
    def __init__(self, host, user, password):
        self.host = host
        self.user = user
        self.password = password
        self.connection = None
    
    def open_connection(self):
        print(f"MySQL: подключение к {self.host}...")
        self.connection = "MySQL connection"
    
    def run_sql(self, sql: str):
        return f"MySQL result for: {sql}"
    
    def disconnect(self):
        print("MySQL: отключение")
        self.connection = None

# Новый PostgreSQL драйвер с совместимым интерфейсом
class PostgreSQLDriver(Database):
    def __init__(self, host, user, password):
        self.host = host
        self.user = user
        self.password = password
    
    def connect(self):
        print(f"PostgreSQL: подключение к {self.host}...")
    
    def execute_query(self, query: str) -> list:
        print(f"PostgreSQL: выполнение {query}")
        return []
    
    def close(self):
        print("PostgreSQL: отключение")

# Адаптер для старого MySQL
class MySQLAdapter(Database):
    def __init__(self, mysql_driver: MySQLOldDriver):
        self.driver = mysql_driver
    
    def connect(self):
        self.driver.open_connection()
    
    def execute_query(self, query: str) -> list:
        result = self.driver.run_sql(query)
        return [result]  # преобразуем в список
    
    def close(self):
        self.driver.disconnect()

# Использование
class DataRepository:
    def __init__(self, db: Database):
        self.db = db
    
    def fetch_users(self):
        self.db.connect()
        results = self.db.execute_query("SELECT * FROM users")
        self.db.close()
        return results

# Можем использовать как MySQL, так и PostgreSQL
mysql_old = MySQLOldDriver('localhost', 'root', 'password')
repository = DataRepository(MySQLAdapter(mysql_old))
users = repository.fetch_users()

# Или PostgreSQL
postgres = PostgreSQLDriver('localhost', 'user', 'password')
repository = DataRepository(postgres)
users = repository.fetch_users()

Пример: Adapter для логирования

from abc import ABC, abstractmethod

class Logger(ABC):
    @abstractmethod
    def log(self, level: str, message: str):
        pass

# Сторонняя библиотека с другим интерфейсом
class CloudLoggingService:
    def send_event(self, severity, text):
        print(f"[CLOUD] {severity.upper()}: {text}")

class CloudLoggingAdapter(Logger):
    def __init__(self, cloud_service: CloudLoggingService):
        self.service = cloud_service
    
    def log(self, level: str, message: str):
        # Преобразуем наш интерфейс в интерфейс облака
        severity_map = {
            'debug': 'debug',
            'info': 'info',
            'warning': 'warn',
            'error': 'error',
        }
        self.service.send_event(severity_map.get(level, 'info'), message)

# Использование
cloud = CloudLoggingService()
logger = CloudLoggingAdapter(cloud)
logger.log('info', 'Application started')
logger.log('error', 'Database connection failed')

Пример: Adapter для систем платежей

class PaymentGateway:
    """Ожидаемый интерфейс"""
    def charge(self, amount: float, card_token: str) -> dict:
        raise NotImplementedError

class StripeGateway(PaymentGateway):
    def charge(self, amount: float, card_token: str) -> dict:
        print(f"Stripe: списываем ${amount}")
        return {'status': 'success', 'transaction_id': 'stripe_123'}

class PayPalAPI:
    """Сторонний API с другим интерфейсом"""
    def execute_payment(self, quantity, item_number, token):
        print(f"PayPal: платёж {quantity} товаров")
        return {'result': 'Complete', 'transactionid': 'paypal_456'}

class PayPalAdapter(PaymentGateway):
    def __init__(self, paypal_api: PayPalAPI):
        self.paypal = paypal_api
    
    def charge(self, amount: float, card_token: str) -> dict:
        # Преобразуем интерфейсы
        result = self.paypal.execute_payment(
            quantity=1,
            item_number='product_1',
            token=card_token
        )
        return {
            'status': 'success' if result['result'] == 'Complete' else 'failed',
            'transaction_id': result['transactionid']
        }

# Использование
stripe = StripeGateway()
paypal_api = PayPalAPI()
paypal = PayPalAdapter(paypal_api)

for gateway in [stripe, paypal]:
    result = gateway.charge(49.99, 'token_123')
    print(f"Result: {result}\n")

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

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

  • Разделение ответственности: адаптер отделяет преобразование интерфейса от основной логики
  • Переиспользование кода: можешь использовать несовместимые классы вместе
  • Гибкость: легко добавлять новые адаптеры без изменения существующего кода
  • SOLID: соответствует Single Responsibility и Open/Closed принципам

Недостатки ❌

  • Усложнение кода: дополнительные классы могут усложнить архитектуру
  • Производительность: небольшой overhead на преобразование интерфейсов
  • Перепроектирование: иногда лучше переписать код, чем добавлять адаптеры

Adapter vs Facade vs Bridge

ПаттернЦельКогдаПример
AdapterСделать несовместимые интерфейсы совместимымиИнтеграция сторонних библиотекStripe → наш PaymentGateway
FacadeУпростить сложный интерфейсСкрыть сложность подсистемыПростое API для сложной БД
BridgeОтделить абстракцию от реализацииРазные реализации одной абстракции렌더инг на разных платформах

Итоги

Адаптер — это мощный паттерн для интеграции несовместимого кода:

  • Object Adapter (композиция) обычно лучше Class Adapter (наследование)
  • Используй для работы с legacy кодом и сторонними библиотеками
  • Помогает соблюдать SOLID принципы, особенно Open/Closed
  • Предпочитай Adapter простому дублированию кода

На практике это один из самых полезных паттернов при разработке, так как постоянно нужно интегрировать несовместимые системы и библиотеки.