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

Ответ

Паттерн Адаптер — это структурный паттерн, который позволяет объектам с несовместимыми интерфейсами работать вместе. Он действует как обёртка (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',
  });
}

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

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

Ответ 18+ 🔞

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

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

Типичная история во 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. Сам адаптер. Он делает вид, что он новый сервис (AnalyticsService),
// а внутри тихонечко, как **хитрая жопа**, дергает старый код.
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);
  }
}

И как мы это используем? Да похуй, теперь всё просто!

void main() {
  // Создаём старый сервис
  final legacyService = LegacyAnalytics();
  // Одеваем на него адаптер, как **хуй в пальто**
  final analytics = LegacyAnalyticsAdapter(legacyService);

  // И вуаля! Мы вызываем новый метод, а адаптер сам всё переведёт.
  analytics.logEvent('button_clicked', parameters: {
    'category': 'ui',
    'action': 'submit',
  });
  // Напечатается: 'Legacy tracking: button_clicked, ui, submit'
}

В чём, блядь, профит?

  • Совместимость наше всё: Можно не переписывать рабочий, но уродливый код. Просто завернул его в адаптер и забыл.
  • Разделение ответственности: Твой основной код теперь не знает про всякие манды с ушами старого интерфейса. Он общается только с красивым, целевым контрактом. Если завтра выкинешь старый сервис — поменяешь только адаптер, а не овердохуища мест в коде.