是Koin依赖注入还是服务定位器?

介绍


传统上,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() {
  // DaggerAppComponent –  ,   Dagger
  val daggerComponent = DaggerAppComponent.create()
  val car = daggerComponent.car()
}

All Articles