A injeção de dependência de Koin ou o localizador de serviço?

Introdução


O desenvolvimento do Android para DI tradicionalmente usa o Dagger 2 , uma estrutura de geração de código muito poderosa. Mas há um problema: é difícil para iniciantes usá-lo. Os princípios de DI são simples e diretos, mas Dagger os complica. Você pode reclamar do declínio absoluto da alfabetização dos programadores, mas o problema não desaparecerá.


Com o advento do Kotlin, tornou-se possível escrever coisas convenientes que seriam quase impossíveis usando Java. Uma dessas coisas foi Koin , que não é um DI, mas um Localizador de Serviços, que é tratado por muitos como antipadrão , e é por isso que muitas pessoas não o usam em princípio. Mas em vão, porque possui uma API muito concisa que simplifica a gravação e a manutenção do código.


Neste artigo, quero ajudar os iniciantes a descobrir a distinção entre Injeção de Dependência e Localizador de Serviço, mas não o próprio Koin.


Injeção de dependência


Primeiro de tudo, o que é injeção de dependência? Em palavras simples, é quando um objeto aceita dependências de fora, em vez de criar ou extraí-las. Vou dar um exemplo. Suponha que tenhamos uma interface Engine, sua implementação e também uma classe Carque depende Engine. Sem DI, pode ser assim


interface Engine
class DefaultEngine: Engine

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

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

Se reescrevermos a classe Carusando a abordagem DI, isso poderá acontecer:


class Car(private val engine: Engine)

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

É simples - a classe Carnão sabe de onde vem a implementação Engine, enquanto a substituição dessa mesma implementação é fácil, basta passá-la ao construtor.


Localizador de serviço


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

Isso é pura injeção de dependência. Com Koin, ficaria assim:


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

Infelizmente, ainda precisamos entrar em contato com Koin para dependências, mas isso não contradiz os princípios da Injeção de Dependências.


ATUALIZAR Por solicitaçãokranid Vou dar o exemplo mais simples do 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