← Назад к вопросам
Что такое сервис локатор?
1.7 Middle🔥 182 комментариев
#Архитектура Flutter#ООП и паттерны
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Service Locator — централизованный реестр сервисов
Service Locator — это паттерн проектирования, который предоставляет центральный реестр для получения экземпляров сервисов и зависимостей. В Flutter чаще всего используется пакет GetIt для реализации этого паттерна.
Основная идея
Service Locator позволяет:
- Централизованное управление зависимостями — один реестр для всего приложения
- Ленивая инициализация — сервисы создаются при первом использовании
- Глобальный доступ — из любого места приложения
- Простая подмена — для тестирования легко заменить реальный сервис на mock
- Декупление — классы не знают о способе создания зависимостей
GetIt в Flutter
Установка:
flutter pub add get_it
Базовое использование
import 'package:get_it/get_it.dart';
final getIt = GetIt.instance;
// Регистрация сервисов в main.dart
void setupServiceLocator() {
// Singleton — один экземпляр на все приложение
getIt.registerSingleton<ApiClient>(ApiClient());
// Lazy singleton — создается при первом использовании
getIt.registerLazySingleton<AuthService>(() => AuthService());
// Factory — каждый раз новый экземпляр
getIt.registerFactory<UserRepository>(() => UserRepository());
}
void main() {
setupServiceLocator();
runApp(MyApp());
}
// Использование в коде
class UserWidget extends StatelessWidget {
final _authService = getIt<AuthService>();
@override
Widget build(BuildContext context) {
return Text(_authService.currentUser.name);
}
}
Практические примеры
1. Установка сервисов (Service Setup Layer):
// lib/config/service_locator.dart
import 'package:get_it/get_it.dart';
final getIt = GetIt.instance;
void setupServiceLocator() {
// Data Layer
getIt.registerLazySingleton<ApiClient>(
() => ApiClient(baseUrl: 'https://api.example.com'),
);
getIt.registerLazySingleton<DatabaseService>(
() => DatabaseService(),
);
// Repository Layer
getIt.registerLazySingleton<UserRepository>(
() => UserRepository(
apiClient: getIt<ApiClient>(),
database: getIt<DatabaseService>(),
),
);
// Use Case Layer
getIt.registerLazySingleton<GetUserUseCase>(
() => GetUserUseCase(getIt<UserRepository>()),
);
// Service Layer
getIt.registerLazySingleton<AuthService>(
() => AuthService(getIt<UserRepository>()),
);
getIt.registerLazySingleton<LoggerService>(
() => LoggerService(),
);
}
2. Использование в UI слое:
// lib/screens/user_screen.dart
class UserScreen extends StatefulWidget {
@override
State<UserScreen> createState() => _UserScreenState();
}
class _UserScreenState extends State<UserScreen> {
late final _userRepository = getIt<UserRepository>();
late final _logger = getIt<LoggerService>();
User? user;
bool isLoading = false;
@override
void initState() {
super.initState();
_loadUser();
}
Future<void> _loadUser() async {
setState(() => isLoading = true);
try {
user = await _userRepository.getUser('123');
_logger.info('User loaded successfully');
} catch (e) {
_logger.error('Failed to load user: $e');
} finally {
setState(() => isLoading = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('User')),
body: isLoading
? Center(child: CircularProgressIndicator())
: user != null
? UserDetails(user: user!)
: Center(child: Text('No user found')),
);
}
}
3. Архитектурная структура:
// Domain Layer (независимо от framework'ов)
abstract class UserRepository {
Future<User> getUser(String id);
}
abstract class GetUserUseCase {
Future<User> call(String id);
}
// Data/Infrastructure Layer
class UserRepositoryImpl implements UserRepository {
final ApiClient apiClient;
final DatabaseService database;
UserRepositoryImpl({
required this.apiClient,
required this.database,
});
@override
Future<User> getUser(String id) async {
try {
// Сначала пытаемся получить из кэша
final cached = await database.getUser(id);
if (cached != null) return cached;
// Затем из API
final user = await apiClient.getUser(id);
await database.saveUser(user);
return user;
} catch (e) {
rethrow;
}
}
}
// Use Case Layer
class GetUserUseCaseImpl implements GetUserUseCase {
final UserRepository repository;
GetUserUseCaseImpl(this.repository);
@override
Future<User> call(String id) => repository.getUser(id);
}
// Service Setup
void setupServiceLocator() {
getIt.registerLazySingleton<ApiClient>(() => ApiClient());
getIt.registerLazySingleton<DatabaseService>(() => DatabaseService());
getIt.registerLazySingleton<UserRepository>(
() => UserRepositoryImpl(
apiClient: getIt<ApiClient>(),
database: getIt<DatabaseService>(),
),
);
getIt.registerLazySingleton<GetUserUseCase>(
() => GetUserUseCaseImpl(getIt<UserRepository>()),
);
}
4. Тестирование с Service Locator:
import 'package:mockito/mockito.dart';
import 'package:flutter_test/flutter_test.dart';
// Mock сервисы
class MockUserRepository extends Mock implements UserRepository {}
class MockLogger extends Mock implements LoggerService {}
void main() {
group('UserScreen Tests', () {
setUp(() {
// Очищаем Service Locator
getIt.reset();
// Регистрируем mock'и
final mockRepository = MockUserRepository();
final mockLogger = MockLogger();
getIt.registerSingleton<UserRepository>(mockRepository);
getIt.registerSingleton<LoggerService>(mockLogger);
});
testWidgets('Should display user when loaded', (WidgetTester tester) async {
// Arrange
final mockRepository = getIt<MockUserRepository>();
when(mockRepository.getUser('123'))
.thenAnswer((_) async => User(id: '123', name: 'John'));
// Act
await tester.pumpWidget(MyApp());
await tester.pumpAndSettle();
// Assert
expect(find.text('John'), findsOneWidget);
});
});
}
Типы регистрации GetIt
// 1. Singleton — один экземпляр на все приложение
getIt.registerSingleton<ApiClient>(ApiClient());
// 2. Lazy Singleton — создается при первом обращении
getIt.registerLazySingleton<AuthService>(() => AuthService());
// 3. Factory — новый экземпляр каждый раз
getIt.registerFactory<UserRepository>(() => UserRepository());
// 4. Conditional — с условиями регистрации
getIt.registerSingletonAsync<DatabaseService>(
() async => await DatabaseService().init(),
);
// 5. С параметрами
getIt.registerFactoryParam<UserBloc, String, void>(
(userId, _) => UserBloc(userId),
);
Плюсы и минусы
Плюсы:
- Простая подмена для тестирования
- Централизованное управление
- Удобный доступ из любого места
- Ленивая инициализация
- Хорошо работает с архитектурой (clean, DDD)
Минусы:
- Скрытые зависимости (не видно из сигнатуры функции)
- Глобальное состояние (как Singleton)
- Требует явной очистки в тестах
- Может усложнить отладку
Альтернативы
Dependency Injection через конструктор:
class UserWidget extends StatelessWidget {
final UserRepository userRepository;
final LoggerService logger;
UserWidget({
required this.userRepository,
required this.logger,
});
}
Provider pattern:
final userRepositoryProvider = Provider<UserRepository>(
(ref) => UserRepository(),
);
Итого
Service Locator (GetIt) — это мощный паттерн для управления зависимостями, который:
- Упрощает разработку сложных приложений
- Облегчает тестирование
- Предоставляет глобальный доступ к сервисам
- Хорошо интегрируется с чистой архитектурой
В современных Flutter приложениях это одна из самых популярных техник управления зависимостями, особенно в средних и больших проектах.