Ist Koin Dependency Injection oder Service Locator?

Einführung


Die Android-Entwicklung für DI verwendet traditionell Dagger 2 , ein sehr leistungsfähiges Framework zur Codegenerierung. Aber es gibt ein Problem: Es ist für Anfänger schwierig, es zu benutzen. Die DI-Prinzipien selbst sind einfach und unkompliziert, aber Dagger kompliziert sie. Sie können sich über den absoluten Rückgang der Alphabetisierung von Programmierern beschweren, aber das Problem wird dadurch nicht verschwinden.


Mit dem Aufkommen von Kotlin wurde es möglich, bequeme Dinge zu schreiben, die mit Java fast unmöglich wären. Eines dieser Dinge war Koin , das kein DI ist, sondern ein Service Locator, das von vielen als Anti-Pattern behandelt wird, weshalb viele Leute es im Prinzip nicht verwenden. Aber vergebens, weil es eine sehr übersichtliche API hat, die das Schreiben und Verwalten von Code vereinfacht.


In diesem Artikel möchte ich Anfängern helfen, den Unterschied zwischen Abhängigkeitsinjektion und Service Locator herauszufinden, aber nicht Koin selbst.


Abhängigkeitsspritze


Was ist die Abhängigkeitsinjektion? In einfachen Worten, dies ist der Fall, wenn ein Objekt Abhängigkeiten von außen akzeptiert, anstatt sie selbst zu erstellen oder zu extrahieren. Ich werde ein Beispiel geben. Angenommen, wir haben eine Schnittstelle Engine, ihre Implementierung und auch eine Klasse Car, die davon abhängt Engine. Ohne DI könnte es so aussehen


interface Engine
class DefaultEngine: Engine

class Car {
  private val engine: Engine = DefaultEngine()
}

fun main() {
  val car = Car()
}

Wenn wir die Klasse Carmit dem DI-Ansatz neu schreiben , kann dies passieren:


class Car(private val engine: Engine)

fun main() {
  val car = Car(DefaultEngine())
}

Es ist einfach - die Klasse Carweiß nicht, woher die Implementierung kommt Engine, während das Ersetzen dieser Implementierung einfach ist. Übergeben Sie sie einfach an den Konstruktor.


Service Locator


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>())
}

Dies ist reine Abhängigkeitsinjektion. Mit Koin würde es so aussehen:


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>()
}

Leider müssen wir Koin immer noch wegen Abhängigkeiten kontaktieren, aber dies widerspricht in keiner Weise den Prinzipien der Abhängigkeitsinjektion.


AKTUALISIEREN Auf Anfragekranid Ich werde das einfachste Beispiel für Dolch 2 geben.


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