← Назад к вопросам

В чем разница между DI и сервис локатор?

1.0 Junior🔥 31 комментариев
#Архитектура Flutter

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между Dependency Injection и Service Locator

Dependency Injection (DI) и Service Locator — это два разных паттерна управления зависимостями в приложении. Обе решают проблему связанности кода, но принципиально различаются подходом.

Dependency Injection (Внедрение зависимостей)

Dependency Injection — это паттерн, при котором зависимости передаются в объект из внешних источников, обычно через конструктор.

// DI подход - зависимость передаётся через конструктор
class UserRepository {
  final DatabaseClient database;
  final Logger logger;

  // Зависимости явно заданы в конструкторе
  UserRepository(this.database, this.logger);

  Future<User> getUser(String id) async {
    logger.info('Fetching user: $id');
    return await database.query('SELECT * FROM users WHERE id = ?', [id]);
  }
}

// Использование
final database = DatabaseClient();
final logger = Logger();
final userRepository = UserRepository(database, logger);

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

  • Явная зависимость — сразу видно, что нужно объекту
  • Легко тестировать — передаём mock объекты
  • Слабая связанность — объект не знает, где создаётся его зависимость
  • Легче понять код — читая конструктор, видишь все зависимости
  • Рефакторинг безопаснее — изменения зависимостей видны сразу
// Тестирование с DI
test('UserRepository fetches user correctly', () {
  final mockDatabase = MockDatabaseClient();
  final mockLogger = MockLogger();
  final repository = UserRepository(mockDatabase, mockLogger);

  when(mockDatabase.query(any, any)).thenAnswer((_) async => mockUser);

  final result = await repository.getUser('123');
  expect(result, equals(mockUser));
});

Service Locator

Service Locator — это паттерн, при котором существует глобальный реестр сервисов. Объект запрашивает нужный сервис у локатора.

// Service Locator подход (например, с пакетом get_it)
import 'package:get_it/get_it.dart';

final getIt = GetIt.instance;

// Регистрируем сервисы
void setupServiceLocator() {
  getIt.registerSingleton<DatabaseClient>(DatabaseClient());
  getIt.registerSingleton<Logger>(Logger());
}

// Service Locator используется внутри класса
class UserRepository {
  late final DatabaseClient _database = getIt<DatabaseClient>();
  late final Logger _logger = getIt<Logger>();

  Future<User> getUser(String id) async {
    _logger.info('Fetching user: $id');
    return await _database.query('SELECT * FROM users WHERE id = ?', [id]);
  }
}

// Использование
setupServiceLocator();
final userRepository = UserRepository();

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

  • Меньше код — не передаём 10 параметров в конструктор
  • Глобальный доступ — сервис доступен откуда угодно
  • Простота — не нужна сложная иерархия для внедрения зависимостей
  • Удобно для больших проектов — централизованное управление зависимостями

Недостатки Service Locator

Service Locator имеет существенные проблемы:

// Проблема 1: Скрытые зависимости
class UserRepository {
  // Неясно, какие зависимости нужны объекту!
  Future<User> getUser(String id) async {
    return await getIt<DatabaseClient>().query(...);
  }
}

// Проблема 2: Сложное тестирование
test('UserRepository test', () async {
  // Нужно настраивать глобальное состояние!
  getIt.reset();
  getIt.registerSingleton<DatabaseClient>(MockDatabaseClient());
  
  final repository = UserRepository();
  // Трудно изолировать тест
});

// Проблема 3: Runtime ошибки вместо compile-time
class MyService {
  void doSomething() {
    // Этот код скомпилируется, но упадёт в runtime!
    final repository = getIt<UserRepository>();
  }
}

Сравнение в таблице

АспектDIService Locator
Явность зависимостейЯвные в конструктореСкрытые внутри кода
ТестируемостьЛегко (передаём mocks)Сложно (настройка глобального состояния)
СвязанностьСлабаяВысокая (зависит от регистрации)
Размер конструктораМожет быть большимМал или вообще нет
Точка отказаCompile timeRuntime
ПроизводительностьНормальнаяОчень быстро
Читаемость кодаВыше (все зависимости видны)Ниже (нужно искать getIt вызовы)

Flutter/Dart библиотеки

Для DI:

// get_it - простой Service Locator
final getIt = GetIt.instance;

// Riverpod - реактивный DI
final userRepositoryProvider = Provider<UserRepository>((ref) {
  return UserRepository(ref.watch(databaseProvider));
});

// Provider - самый простой для Flutter
final databaseProvider = Provider<Database>((ref) {
  return Database();
});

Лучший подход

Рекомендация: используй Dependency Injection

// Правильный подход для Flutter
class MyApp extends StatelessWidget {
  final UserRepository userRepository;
  final NotificationService notificationService;
  
  const MyApp({
    required this.userRepository,
    required this.notificationService,
  });
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(
        userRepository: userRepository,
        notificationService: notificationService,
      ),
    );
  }
}

// При необходимости используй Service Locator только для top-level инициализации
void main() {
  setupServiceLocator();
  
  final userRepository = getIt<UserRepository>();
  final notificationService = getIt<NotificationService>();
  
  runApp(MyApp(
    userRepository: userRepository,
    notificationService: notificationService,
  ));
}

Ключевое отличие: DI делает зависимости явными и тестируемыми, Service Locator скрывает их и усложняет тестирование. В профессиональной разработке предпочитают DI, Service Locator используют осторожно, только где это действительно упрощает архитектуру.

В чем разница между DI и сервис локатор? | PrepBro