DI vs Service Locator - makstron/info GitHub Wiki
Прежде всего, что такое Dependency Injection? Простыми словами, это когда объект принимает зависимости извне, а не создаёт или добывает их сам. Приведу пример. Предположим, у нас есть интерфейс Engine, его реализация, а также класс Car, который зависит от Engine. Без использования DI это может выглядеть вот так
interface Engine
class DefaultEngine: Engine
class Car {
private val engine: Engine = DefaultEngine()
}
fun main() {
val car = Car()
}
Если переписать класс Car с использованием подхода DI, то может получиться вот это:
class Car(private val engine: Engine)
fun main() {
val car = Car(DefaultEngine())
}
Всё просто – класс Car не знает, откуда приходит реализация Engine, при этом заменить эту самую реализацию легко, достаточно передать её в конструктор.
Попробуем разобраться с Service Locator. Тут тоже ничего сложного – это некий реестр, который по запросу может предоставить нужный объект.
object ServiceLocator {
fun <reified T> register(factory: () -> T) { ... }
fun <reified T> resolve(): T { ... }
}
У нас есть возможность добавить в наш реестр некую зависимость, а также получить эту зависимость. Вот пример использования с нашими двигателями и машинами:
interface Engine
class DefaultEngine: Engine
class Car {
private val engine: Engine = ServiceLocator.resolve()
}
fun main() {
ServiceLocator.register<Engine> { DefaultEngine() }
val car = Car()
}
Это отдалённо похоже на DI, ведь класс Car получает зависимость извне, не зная о реализации, но у данного подхода есть проблема – мы ничего не знаем о зависимостях класса Car, есть ли они вообще. Можно попробовать такой подход:
interface ServiceLocator {
fun <reified T> register(factory: () -> T)
fun <reified T> resolve(): T
}
class DefaultServiceLocator: ServiceLocator { ... }
class Car(private val serviceLocator: ServiceLocator) {
private val engine = serviceLocator.resolve<Engine>()
}
fun main() {
val serviceLocator = DefaultServiceLocator()
serviceLocator.register<Engine> { DefaultEngine() }
val car = Car(serviceLocator)
}
Теперь мы знаем, что у Car есть зависимости, но всё ещё не знаем какие. Т.е. это не решение нашей проблемы. Но есть ещё один вариант:
class Car(private val engine: Engine)
fun main() {
ServiceLocator.register<Engine> { DefaultEngine() }
val car = Car(ServiceLocator.resolve<Engine>())
}