介绍
传统上,DI的Android开发使用Dagger 2(一种功能非常强大的代码生成框架)。但是有一个问题:初学者很难使用它。DI原理本身简单明了,但Dagger使它们复杂化。您可以抱怨程序员的素养绝对下降,但是问题不会因此而消失。
随着Kotlin的出现,可以编写方便的东西,而使用Java几乎是不可能的。其中之一就是Koin,它不是DI,而是服务定位器,被许多人视为反模式,这就是为什么许多人原则上不使用它的原因。但是徒劳无功,因为它具有非常简洁的API,可以简化代码的编写和维护。
在本文中,我想帮助初学者弄清楚依赖注入和服务定位器之间的区别,而不是Koin本身。
依赖注入
首先,什么是依赖注入?简单来说,就是对象接受外部依赖,而不是自己创建或提取依赖。我举一个例子。假设我们有一个接口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. – , . Koin ServiceLocator, , API.
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>())
}
这是纯依赖注入。使用Koin,它看起来像这样:
interface Engine
class DefaultEngine: Engine
class Car(private val engine: Engine)
fun carModule() = module {
factory<Engine> { DefaultEngine() }
factory { Car(get<Engine>()) }
}
fun main() {
val koin = startKoin {
modules(carModule())
}.koin
val car = koin.get<Car>()
}
las,我们仍然需要联系Koin以获得依赖关系,但这绝不与依赖关系注入的原则相抵触。
更新 按要求克拉尼德 我将在Dagger 2上给出最简单的示例。
interface Engine
class DefaultEngine: Engine
class Car @Inject constructor(private val engine: Engine)
@Module
class AppModule {
@Provides
fun provideEngine(): Engine = DefaultEngine()
}
@Component(modules = [AppModule.class])
interface AppComponent {
fun car(): Car
}
fun main() {
val daggerComponent = DaggerAppComponent.create()
val car = daggerComponent.car()
}