'Est-ce que l'injection de dépendance Koin ou le localisateur de service?

introduction


Le développement Android pour DI utilise traditionnellement Dagger 2 , un framework de génération de code très puissant. Mais il y a un problème: il est difficile pour les débutants de l'utiliser. Les principes DI eux-mêmes sont simples et directs, mais Dagger les complique. Vous pouvez vous plaindre du déclin absolu de l'alphabétisation des programmeurs, mais le problème n'en disparaîtra pas.


Avec l'avènement de Kotlin, il est devenu possible d'écrire des choses pratiques qui seraient presque impossibles à utiliser Java. L'une de ces choses était Koin , qui n'est pas un DI, mais un localisateur de service, qui est traité par beaucoup comme anti-modèle , c'est pourquoi beaucoup de gens ne l'utilisent pas en principe. Mais en vain, car il dispose d'une API très concise qui simplifie l'écriture et la maintenance du code.


Dans cet article, je veux aider les débutants à comprendre la distinction entre l'injection de dépendance et le localisateur de services, mais pas Koin lui-même.


Injection de dépendance


Tout d'abord, qu'est-ce que l'injection de dépendance? En termes simples, c'est lorsqu'un objet accepte les dépendances de l'extérieur, plutôt que de les créer ou de les extraire lui-même. Je vais vous donner un exemple. Supposons que nous ayons une interface Engine, son implémentation et aussi une classe Carqui en dépend Engine. Sans DI, cela pourrait ressembler à ceci


interface Engine
class DefaultEngine: Engine

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

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

Si nous réécrivons la classe en Carutilisant l'approche DI, cela peut se produire:


class Car(private val engine: Engine)

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

C'est simple - la classe Carne sait pas d'où vient l'implémentation Engine, alors que le remplacement de cette implémentation est facile, il suffit de la transmettre au constructeur.


Localisateur de services


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

Il s'agit d'une pure injection de dépendance. Avec Koin, cela ressemblerait à ceci:


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

Hélas, nous devons toujours contacter Koin pour les dépendances, mais cela ne contredit en rien les principes de l'injection de dépendance.


MISE À JOUR Sur demandekranid Je vais donner l'exemple le plus simple sur 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