Что такое ключевое слово implements в Dart?

Ответ

implements — это ключевое слово в Dart, которое заставляет класс выполнять контракт (интерфейс) другого класса. В отличие от extends, класс, использующий implements, не наследует реализацию методов и полей. Он обязан предоставить собственную реализацию всех публичных членов (методов, геттеров, сеттеров) имплементируемого класса.

Ключевые особенности:

  • Множественная имплементация: Класс может реализовывать несколько интерфейсов через запятую (implements A, B, C).
  • Строгий контракт: Все методы интерфейса должны быть явно объявлены и реализованы.
  • Полиморфизм: Объект может использоваться через тип своего интерфейса.

Практический пример в Flutter: Создание различных стратегий для сервиса кэширования.

// Определяем интерфейс (контракт) для кэша
abstract class CacheService {
  Future<void> write(String key, String value);
  Future<String?> read(String key);
  Future<void> delete(String key);
}

// Первая реализация: кэш в оперативной памяти
class MemoryCacheService implements CacheService {
  final Map<String, String> _cache = {};

  @override
  Future<void> write(String key, String value) async {
    _cache[key] = value;
  }

  @override
  Future<String?> read(String key) async {
    return _cache[key];
  }

  @override
  Future<void> delete(String key) async {
    _cache.remove(key);
  }
}

// Вторая реализация: кэш с использованием пакета shared_preferences
import 'package:shared_preferences/shared_preferences.dart';

class SharedPrefsCacheService implements CacheService {
  @override
  Future<void> write(String key, String value) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(key, value);
  }

  @override
  Future<String?> read(String key) async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getString(key);
  }

  @override
  Future<void> delete(String key) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove(key);
  }
}

// Использование в коде
class DataRepository {
  final CacheService cacheService;
  DataRepository(this.cacheService); // Внедряем зависимость через интерфейс

  Future<String> fetchData() async {
    const key = 'my_data';
    final cached = await cacheService.read(key);
    if (cached != null) return cached;
    // ... загрузка с сети
    final newData = 'Данные из сети';
    await cacheService.write(key, newData);
    return newData;
  }
}
// Теперь можно легко подменить реализацию кэша
final repoWithMemoryCache = DataRepository(MemoryCacheService());
final repoWithPrefsCache = DataRepository(SharedPrefsCacheService());

Итог: implements — мощный инструмент для соблюдения контрактов, обеспечения слабой связанности и легкого тестирования (мокинга) в Dart.

Ответ 18+ 🔞

Давай разберёмся, что это за зверь такой — implements. Представь, что ты нанимаешь работника. Ты ему говоришь: «Слушай, чувак, вот тебе должностная инструкция — ты должен уметь делать вот это, вот это и вот это». А он тебе в ответ: «Да похуй, я сам знаю, как это делать, главное — результат будет как надо». Вот implements — это и есть эта самая инструкция, контракт. Ты не получаешь готовых инструментов от начальника, ты обязан принести свои и сделать всё так, как договорились.

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

Что важно запомнить:

  • Можно набрать овердохуища инструкций: Класс может взять на себя обязательства сразу от нескольких интерфейсов, через запятую. Как будто ты соглашаешься быть и сантехником, и электриком, и ещё кем-нибудь.
  • Контракт — это святое: Если пообещал метод — будь добр, реализуй его полностью, со всеми параметрами. Иначе компилятор тебе устроит терпения ноль ебать и просто не скомпилирует.
  • Полиморфизм, ёпта: Объект твоего класса можно будет тыкать везде, где ожидается тип интерфейса. Система будет думать: «А, этот чувак умеет то, что мне нужно, и ладно».

Пример из жизни Flutter, чтобы не быть голословным: Допустим, нам нужен кэш. Но мы хитрая жопа и не хотим жёстко привязываться к одному способу хранения. Сегодня — память, завтра — база данных.

// Вот наш контракт, наша должностная инструкция для любого кэша.
abstract class CacheService {
  Future<void> write(String key, String value);
  Future<String?> read(String key);
  Future<void> delete(String key);
}

// Первый работник: кэш, который всё хранит в оперативке. Быстро, но после перезагрузки — **накрылся медным тазом**.
class MemoryCacheService implements CacheService {
  final Map<String, String> _cache = {};

  @override
  Future<void> write(String key, String value) async {
    _cache[key] = value; // Записал в мапу и свободен.
  }

  @override
  Future<String?> read(String key) async {
    return _cache[key]; // Достал из мапы — или значение, или null.
  }

  @override
  Future<void> delete(String key) async {
    _cache.remove(key); // Выкинул ключ нахуй из мапы.
  }
}

// Второй работник: кэш, который лезет в shared_preferences. Медленнее, но переживает перезапуск приложения.
import 'package:shared_preferences/shared_preferences.dart';

class SharedPrefsCacheService implements CacheService {
  @override
  Future<void> write(String key, String value) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(key, value); // Поехали работать с платформой.
  }

  @override
  Future<String?> read(String key) async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getString(key); // Достаём из префов, что бог послал.
  }

  @override
  Future<void> delete(String key) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove(key); // Чистим за собой.
  }
}

// А вот как этим пользоваться. Мы создаём репозиторий, который вообще не парится, КАКОЙ именно кэш ему дадут.
class DataRepository {
  final CacheService cacheService; // Ему главное, чтобы контракт соблюдался.
  DataRepository(this.cacheService);

  Future<String> fetchData() async {
    const key = 'my_data';
    final cached = await cacheService.read(key);
    if (cached != null) {
      print('Достал из кэша, можно не париться!');
      return cached;
    }
    // ... тут код, который лезет в сеть, если в кэше пусто
    final newData = 'Свежие данные с сервака';
    await cacheService.write(key, newData);
    return newData;
  }
}

// И теперь мы можем, как короли, выбирать, какой кэш использовать. Это же **удовольствие ебать**!
final repoWithMemoryCache = DataRepository(MemoryCacheService()); // Быстро, но временно.
final repoWithPrefsCache = DataRepository(SharedPrefsCacheService()); // Медленнее, но надёжно.

Итог, блядь: implements — это твой лучший друг, когда нужно сделать код гибким, отвязаться от конкретной реализации и потом легко это всё тестировать, подсовывая заглушки. Не наследуй, а обещай и выполняй.