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 Car
mit 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 Car
weiß 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() {
val daggerComponent = DaggerAppComponent.create()
val car = daggerComponent.car()
}