← Назад к вопросам
Что такое паттерн Адаптер (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 простому дублированию кода
На практике это один из самых полезных паттернов при разработке, так как постоянно нужно интегрировать несовместимые системы и библиотеки.