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

«Что такое паттерн Адаптер (Adapter)?» — вопрос из категории Паттерны, который задают на 29% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Типичный сценарий во Flutter: интеграция старого или стороннего кода, который нельзя изменить, в новую систему с другим контрактом интерфейса.

Пример: Адаптация старого сервиса аналитики под новый интерфейс.

// 1. Новый целевой интерфейс, который ожидает наше приложение
abstract class AnalyticsService {
  void logEvent(String eventName, {Map<String, dynamic>? parameters});
}

// 2. Старый (адаптируемый) класс со своим несовместимым интерфейсом
class LegacyAnalytics {
  // У него другой метод с другим набором параметров
  void track(String event, String category, String action) {
    print('Legacy tracking: $event, $category, $action');
  }
}

// 3. Адаптер, который реализует новый интерфейс, но внутри использует старый класс
class LegacyAnalyticsAdapter implements AnalyticsService {
  final LegacyAnalytics _legacyService;

  LegacyAnalyticsAdapter(this._legacyService);

  @override
  void logEvent(String eventName, {Map<String, dynamic>? parameters}) {
    // Преобразуем вызов нового интерфейса в формат старого класса
    String category = parameters?['category'] ?? 'defaultCategory';
    String action = parameters?['action'] ?? 'defaultAction';
    _legacyService.track(eventName, category, action);
  }
}

// 4. Использование в коде приложения
void main() {
  final legacyService = LegacyAnalytics();
  final analytics = LegacyAnalyticsAdapter(legacyService); // Обернули в адаптер

  // Теперь мы можем использовать единый современный интерфейс
  analytics.logEvent('button_clicked', parameters: {
    'category': 'ui',
    'action': 'submit',
  });
}

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

  • Совместимость: Позволяет повторно использовать существующий код.
  • Отделение логики: Клиентский код зависит только от целевого интерфейса, а не от деталей реализации адаптируемого класса.