Что такое паттерн Адаптер в программировании?

Ответ

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

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

Типы Адаптеров:

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

Пример (Object Adapter на Python): Представим, что у нас есть старая система логирования, которая принимает сообщения только через метод log_message_old_format, а новая система ожидает метод log_message.

# Адаптируемый класс (Adaptee) - старая система
class OldLogger:
    def log_message_old_format(self, message: str):
        print(f"[OLD LOG]: {message}")

# Целевой интерфейс (Target) - что ожидает клиент
class NewLoggerInterface:
    def log_message(self, message: str):
        raise NotImplementedError("Должен быть реализован подклассом")

# Адаптер (Adapter)
class LoggerAdapter(NewLoggerInterface):
    def __init__(self, old_logger: OldLogger):
        self._old_logger = old_logger

    def log_message(self, message: str):
        # Преобразуем вызов нового интерфейса в вызов старого
        self._old_logger.log_message_old_format(f"[ADAPTED] {message}")

# Клиентский код, который работает с новым интерфейсом
def client_code(logger: NewLoggerInterface, text: str):
    logger.log_message(text)

# Использование:
old_logger_instance = OldLogger()

# Клиент не может напрямую использовать OldLogger:
# client_code(old_logger_instance, "Это сообщение не сработает") # Ошибка атрибута

# Используем адаптер для совместимости
adapter = LoggerAdapter(old_logger_instance)
client_code(adapter, "Сообщение через адаптер")

# Вывод:
# [OLD LOG]: [ADAPTED] Сообщение через адаптер

Когда использовать паттерн Адаптер?

  • Интеграция: Когда нужно использовать существующий класс, но его интерфейс не соответствует потребностям.
  • Сторонние библиотеки: При работе с библиотеками, которые имеют полезную функциональность, но неудобный или несовместимый API.
  • Унификация: Для предоставления единого интерфейса к нескольким классам с похожей, но не идентичной функциональностью.

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

  • Повторное использование кода: Позволяет использовать существующие классы без их модификации.
  • Разделение ответственности: Отделяет логику преобразования интерфейсов от основной бизнес-логики.
  • Гибкость: Позволяет легко заменять адаптируемые классы, не затрагивая клиентский код.

Недостатки:

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