Ответ
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 — это твой лучший друг, когда нужно сделать код гибким, отвязаться от конкретной реализации и потом легко это всё тестировать, подсовывая заглушки. Не наследуй, а обещай и выполняй.