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

В чем разница между Service Locator и DI?

2.0 Middle🔥 121 комментариев
#Dependency Injection#Архитектура и паттерны

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

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

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

В чем разница между Service Locator и DI?

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

Service Locator

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

// Service Locator паттерн
class ServiceLocator {
    private val services = mutableMapOf<Class<*>, Any>()
    
    fun <T> register(clazz: Class<T>, instance: T) {
        services[clazz] = instance as Any
    }
    
    fun <T> get(clazz: Class<T>): T {
        return services[clazz] as T
    }
}

val locator = ServiceLocator()
locator.register(UserRepository::class.java, UserRepositoryImpl())
locator.register(UserService::class.java, UserService(locator))

// Использование
class MyViewModel {
    private val userService = locator.get(UserService::class.java)
}

Dependency Injection

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

// Dependency Injection паттерн
class UserService(private val userRepository: UserRepository) {
    fun getUser(id: String) = userRepository.getUser(id)
}

class MyViewModel(private val userService: UserService) {
    fun loadUser(id: String) = userService.getUser(id)
}

// Использование
val repository = UserRepositoryImpl()
val service = UserService(repository)
val viewModel = MyViewModel(service)

Ключевые различия

АспектService LocatorDependency Injection
Получение зависимостейПросишь у локатораПередаёшь при создании
Контроль над зависимостямиНеявный (скрытый в коде)Явный (видно в конструкторе)
ТестированиеСложнее (нужно мокировать локатор)Легче (просто передаёшь mock)
Видимость зависимостейСкрыта внутри кодаВидна в сигнатуре
СвязностьВысокая (зависимость от локатора)Низкая (только от интерфейсов)
ПроизводительностьБыстро при малом количестве типовМинимальный overhead

Тестирование

Service Locator — сложнее:

// Тестирование с Service Locator
@Test
fun testUserViewModel() {
    val mockRepository = mockk<UserRepository>()
    every { mockRepository.getUser(any()) } returns User(1, "Test")
    
    // Нужно заменить в локаторе
    locator.register(UserRepository::class.java, mockRepository)
    
    val viewModel = MyViewModel() // внутри она достанет из локатора
    // Проблема: скрыто в коде, где именно берётся mock?
}

DI — проще:

// Тестирование с DI
@Test
fun testUserViewModel() {
    val mockRepository = mockk<UserRepository>()
    every { mockRepository.getUser(any()) } returns User(1, "Test")
    
    val service = UserService(mockRepository)
    val viewModel = MyViewModel(service)
    
    // Ясно, что тестируется с mock
    assertEquals(expected, viewModel.loadUser("1"))
}

Android примеры

Service Locator (Koin когда-то использовал этот паттерн):

val myModule = module {
    single { UserRepositoryImpl() as UserRepository }
    factory { UserService(get()) }
}

startKoin { modules(myModule) }

class MyViewModel : ViewModel() {
    private val userService: UserService = get()
}

DI (Hilt — рекомендуемый подход):

@HiltViewModel
class MyViewModel @Inject constructor(
    private val userService: UserService
) : ViewModel() {}

@Provides
@Singleton
fun provideUserRepository(): UserRepository = UserRepositoryImpl()

@Provides
fun provideUserService(repository: UserRepository) = UserService(repository)

Проблемы Service Locator

  1. Скрытые зависимости — не видно из сигнатуры, какие зависимости нужны
  2. Сложнее тестировать — нужно мокировать локатор
  3. Runtime ошибки — если забыл зарегистрировать класс, ошибка только при вызове
  4. Global state — локатор это по сути глобальное состояние
  5. Порядок регистрации — может быть важен порядок регистрации

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

  1. Явные зависимости — видно в конструкторе
  2. Легче тестировать — просто передаёшь mock
  3. Compile-time ошибки — если забыл передать зависимость, IDE ошибку покажет
  4. Нет глобального state — каждый объект независим
  5. Гибкость — каждому объекту свои зависимости

Мой совет

На современном Android используй Dependency Injection через Hilt. Это Google-official решение, которое:

  • Интегрировано с MVVM
  • Поддерживает все komponenty Android
  • Имеет compile-time safety
  • Легче тестировать
  • Это де-факто стандарт

Service Locator — это anti-pattern в современной разработке. Он был популярен когда DI фреймворков не было, но сейчас это не рекомендуется.

В чем разница между Service Locator и DI? | PrepBro