Kotlin-függőségi injekció Kodeinnal

1. Áttekintés

Ebben a cikkben bemutatjuk a Kodeint - egy tiszta Kotlin-függőségi injekció (DI) keretrendszert -, és összehasonlítjuk más népszerű DI-keretrendszerekkel.

2. Függőség

Először tegyük hozzá a Kodein-függőséget a sajátunkhoz pom.xml:

 com.github.salomonbrys.kodein kodein 4.1.0 

Felhívjuk figyelmét, hogy a legújabb elérhető verzió elérhető a Maven Central vagy a jCenter webhelyen.

3. Konfiguráció

Az alábbi modellt használjuk a Kodein-alapú konfiguráció bemutatására:

osztály Vezérlő (privát val service: Service) osztály Service (private val dao: Dao, private val tag: String) interfész Dao osztály JdbcDao: Dao osztály MongoDao: Dao

4. Kötési típusok

A Kodein keretrendszer különféle kötéstípusokat kínál. Vizsgáljuk meg közelebbről, hogyan működnek és hogyan használják őket.

4.1. Szingli

Val vel Szingli kötés, a cél babot lustán példányosítják az első hozzáféréskor és minden további kérésre felhasználják:

var létre = hamis; val kodein = Kodein {bind () singletonnal {MongoDao ()}} assertThat (létrehozva) .isFalse () val dao1: Dao = kodein.instance () assertThat (létrehozva) .isFalse () val dao2: Dao = kodein.instance () assertThat (dao1) .isSameAs (dao2)

Megjegyzés: használhatjuk Kodein.instance () statikus változótípus alapján célzottan kezelt babok lekérésére.

4.2. Vágyakozó Singleton

Ez hasonló a Szingli kötés. Az egyetlen különbség az az inicializálási blokkot lelkesen hívják:

var létre = hamis; val kodein = Kodein {bind () singletonnal {MongoDao ()}} assertThat (létrehozva) .isTrue () val dao1: Dao = kodein.instance () val dao2: Dao = kodein.instance () assertThat (dao1) .isSameAs (dao2)

4.3. Gyár

Val vel Gyár kötés, az inicializáló blokk argumentumot kap, és minden alkalommal új tárgyat adnak vissza belőle:

val kodein = Kodein {bind () singletonnal {MongoDao ()} bind () gyárilag {tag: String -> Service (instance (), tag)}} val service1: Service = kodein.with ("myTag"). instance () val service2: Service = kodein.with ("myTag"). instance () assertThat (service1) .isNotSameAs (service2)

Megjegyzés: használhatjuk Kodein.instance () a tranzitív függőségek konfigurálásához.

4.4. Multiton

Multiton a kötés nagyon hasonlít a Gyár kötés. Az egyetlen különbség az ugyanazt az objektumot adja vissza ugyanazon argumentumért a következő hívások során:

val kodein = Kodein {bind () singletonnal {MongoDao ()} bind () multiton {tag: String -> Service (instance (), tag)}} val service1: Service = kodein.with ("myTag"). instance () val service2: Service = kodein.with ("myTag"). instance () assertThat (service1) .isSameAs (service2)

4.5. Szolgáltató

Ez egy no-arg Gyár kötés:

val kodein = Kodein {bind () {MongoDao ()}} szolgáltatóval val dao1: Dao = kodein.instance () val dao2: Dao = kodein.instance () assertThat (dao1) .isNotSameAs (dao2)

4.6. Példa

Tudunk regisztráljon egy előre konfigurált babpéldányt a tartályban:

val dao = MongoDao () val kodein = Kodein {bind () példánnyal (dao)} val fromContainer: Dao = kodein.instance () assertThat (dao) .isSameAs (fromContainer)

4.7. Címkézés

Azt is megtehetjük regisztráljon több, azonos típusú babot különböző címkék alatt:

val kodein = Kodein {kötés ("dao1") szingulettel {MongoDao ()} kötés ("dao2") szingulettel {MongoDao ()}} val dao1: Dao = kodein.instance ("dao1") val dao2: Dao = kodein.instance ("dao2") assertThat (dao1) .isNotSameAs (dao2)

4.8. Állandó

Ez szintaktikus cukor a címkézett kötéssel szemben, és feltételezzük konfigurációs állandókhoz használható - egyszerű típusok öröklés nélkül:

val kodein = Kodein {állandó ("varázslat") 42-vel} val fromContainer: Int = kodein.instance ("magic") assertThat (fromContainer) .isEqualTo (42)

5. Kötések elválasztása

A Kodein lehetővé teszi számunkra, hogy a babokat külön blokkokban konfiguráljuk és kombináljuk.

5.1. Modulok

Tudunk csoportosítsa az összetevőket bizonyos szempontok szerint - például az összes adatmegmaradással kapcsolatos osztály - és a blokkokat kombinálva készítsen egy kapott konténert:

val jdbcModule = Kodein.Module {bind () singletonnal {JdbcDao ()}} val kodein = Kodein {import (jdbcModule) bind () singletonnal {Controller (instance ())} bind () singleton {Service (instance ( ), "myService")}} val dao: Dao = kodein.instance () assertThat (dao) .isInstanceOf (JdbcDao :: class.java)

Megjegyzés: mivel a modulok kötelező érvényű szabályokat tartalmaznak, a célbabok újra létrejönnek, ha ugyanazt a modult több Kodein példányban használják.

5.2. Fogalmazás

Kiterjeszthetjük az egyik Kodein példányt a másikról - ez lehetővé teszi számunkra a bab újrafelhasználását:

val persistenceContainer = Kodein {bind () singletonnal {MongoDao ()}} val serviceContainer = Kodein {extension (persistenceContainer) bind () singletonnal {Service (instance (), "myService")}} val fromPersistence: Dao = persistenceContainer. instance () val fromService: Dao = serviceContainer.instance () assertThat (fromPersistence) .isSameAs (fromService)

5.3. Felülbíráló

Felülírhatjuk a kötéseket - ez hasznos lehet a teszteléshez:

class InMemoryDao: Dao val commonModule = Kodein.Module {bind () singletonnal {MongoDao ()} bind () singleton {Service (instance (), "myService")}} val testContainer = Kodein {import (commonModule) bind ( felülbírálja = igaz) szingulettel {InMemoryDao ()}} val dao: Dao = testContainer.instance () assertThat (dao) .isInstanceOf (InMemoryDao :: class.java)

6. Több kötés

Konfigurálhatjuk több, ugyanazzal a közös (szuper-) típussal rendelkező bab a tartályban:

val kodein = Kodein {bind () from setBinding () bind (). inSet () singletonnal {MongoDao ()} bind (). inSet () singletonnal {JdbcDao ()}} val daos: Set = kodein.instance ( ) assertThat (daos.map {it.javaClass as Class}) .conconOnly (MongoDao :: class.java, JdbcDao :: class.java)

7. Injektor

Alkalmazási kódunk nem ismerte a Kodeint az összes példában, amelyet korábban használtunk - rendszeres konstruktor argumentumokat használt, amelyeket a tároló inicializálása során adtak meg.

A keret azonban lehetővé teszi a függőségek konfigurálásának alternatív módja a delegált tulajdonságok és a Injektorok:

class Controller2 {private val injector = KodeinInjector () val service: Service with injector.instance () fun injectDependencies (kodein: Kodein) = injector.inject (kodein)} val kodein = Kodein {bind () szingulettel {MongoDao ()} bind () singletonnal {Service (instance (), "myService")}} val kontroller = Controller2 () controller.injectDependencies (kodein) assertThat (controller.service) .isNotNull

Más szavakkal, egy tartományosztály egy injektoron keresztül határozza meg a függőségeket, és egy adott tárolóból szerzi be azokat. Ez a megközelítés olyan speciális környezetekben hasznos, mint az Android.

8. A Kodein használata Android rendszerrel

Az Android rendszerben a Kodein tároló egy egyedi konfigurációban van Alkalmazás osztályba, később pedig a Kontextus példa. Feltételezzük, hogy minden komponens (tevékenység, töredék, szolgáltatás, műsorszóró vevő) kiterjesztésre kerül a hasonló segédprogram osztályokból KodeinActivity és KodeinTöredék:

class MyActivity: Activity (), KodeinInjected {override val injector = KodeinInjector () val random: Véletlenszerű példányonként () felülírja a fun onCreate (savedInstanceState: Bundle?) {inject (appKodein ())}}

9. Elemzés

Ebben a részben meglátjuk, hogy viszonyul a Kodein a népszerű DI keretrendszerekhez.

9.1. Tavaszi keret

A Spring Framework sokkal funkciógazdagabb, mint a Kodein. Például a Tavasznak van egy nagyon kényelmes alkatrész-szkennelési lehetőség. Amikor osztályainkat olyan jelölésekkel jelöljük, mint például @Összetevő, @Szolgáltatás, és @Nevezett, a komponens szkennelés automatikusan felveszi ezeket az osztályokat a tároló inicializálása során.

A tavasznak is van hatékony meta-programozási kiterjesztési pontok, BeanPostProcessor és BeanFactoryPostProcessor, ami kulcsfontosságú lehet egy konfigurált alkalmazás adott környezethez történő adaptálásakor.

Végül a Spring nyújt néhányat a tetejére épített kényelmes technológiák, beleértve az AOP-t, a Tranzakciókat, a Test Framework-et és még sokan mások. Ha ezeket használni akarjuk, érdemes ragaszkodni a tavaszi IoC tartályhoz.

9.2. 2. tőr

A Dagger 2 keretrendszer az nem olyan gazdag funkciókban, mint a Spring Framework, de népszerű az Android fejlesztésében sebessége (Java kódot generál, amely végrehajtja az injekciót és csak futás közben hajtja végre) és mérete miatt.

Hasonlítsuk össze a könyvtárak metódusszámát és méretét:

Kodein:Vegye figyelembe, hogy a kotlin-stdlib ezeknek a számoknak a nagy részét a függőség teszi ki. Ha kizárjuk, 1282 metódust és 244 KB DEX méretet kapunk.

2. tőr:

Láthatjuk, hogy a Dagger 2 keretrendszer sokkal kevesebb metódust ad hozzá, és a JAR fájlja kisebb.

Ami a felhasználást illeti - nagyon hasonló abban, hogy a felhasználói kód függőségeket konfigurál (keresztül Injektor a Kodein és a JSR-330 annotációkban a Dagger 2-ben), majd később egyetlen módszerrel hívják őket be.

A Dagger 2 legfontosabb jellemzője azonban, hogy az validálja a függőségi grafikont fordításkor, így konfigurációs hiba esetén nem teszi lehetővé az alkalmazás lefordítását.

10. Következtetés

Most már tudjuk, hogyan kell használni a Kodeint a függőség injektálásához, milyen konfigurációs lehetőségeket kínál, és hogyan hasonlítható össze néhány más népszerű DI keretrendszerrel. Azonban rajtad múlik, hogy felhasználja-e valódi projektekben.

Mint mindig, a fenti minták forráskódja a GitHub oldalon található.