Slide 1

Slide 1 text

Dependency Injection with KODEIN Ubiratan Soares January / 2017

Slide 2

Slide 2 text

DI 101

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Dependency Injection is all about a separation of concerns between some object creation and usage

Slide 5

Slide 5 text

class Controller(private val source: DataSource) { fun doSomeWork() { val service = Service(ds) val formatter = OutputFormatter() render(formatter.format(service.getResults())) } }

Slide 6

Slide 6 text

class Controller(private val source: DataSource) { fun doSomeWork() { val service = Service(ds) val formatter = OutputFormatter() render(formatter.format(service.getResults())) } }

Slide 7

Slide 7 text

class Controller( private val service: Service, private val formatter: OutputFormatter) { fun doSomeWork() { val results = service.getResults() render(formatter.format(results)) } } class Service(private val source: DataSource) { } class DataSource( private val logger: Logger, private val tracker: ErrorTracker, // other dependencies ) class OutputFormatter { }

Slide 8

Slide 8 text

class Controller( private val service: Service, private val formatter: OutputFormatter) { fun doSomeWork() { val results = service.getResults() render(formatter.format(results)) } } class Service(private val source: DataSource) { } class DataSource( private val logger: Logger, private val tracker: ErrorTracker, // other dependencies ) class OutputFormatter { }

Slide 9

Slide 9 text

Enforcing DI means that any class with an available public constructor gets all its collaborators at instantiation time

Slide 10

Slide 10 text

"NO MERCY !!! NO PRISIONERS!!! ALL SINGLETONS MUST DIE !!!"

Slide 11

Slide 11 text

"Dont call us, we call you" The Hollywood Principle

Slide 12

Slide 12 text

Data Source Controller Service External RestAPI Error Tracker Logger Data Parser Cache Colaborates with

Slide 13

Slide 13 text

Logger Controller Service DataSource DataParser ErrorTracker Cache External API Direct Acyclic Graph

Slide 14

Slide 14 text

Dependencies Chain Controller A Dependencies Chain Controller B Dependencies Chain Controller N Application …

Slide 15

Slide 15 text

WHO BUILDS THE OBJECT GRAPH ???

Slide 16

Slide 16 text

Usually the application itself bootstraps the dependencies graph at launch time

Slide 17

Slide 17 text

• Manual code setup of all objects creation and DAG relationships • Extra code needed for scoped- based dependencies • Reflection-free • No DI errors at runtime • Boilterplate • Semi automated code setup, driven by JSR330 annotations standard • Reflection-based (Guice, etc) or reflection-free (Dagger2) • Reflection-based solutions may crash at runtime due to dependency resolution errors • Less code than static factories Static Factories JSR330

Slide 18

Slide 18 text

Dependency Injection and Dependency Inversion are two distinct concepts, but related in practice

Slide 19

Slide 19 text

The Dependency Inversion Principle (from SOLID) "High level policies should not depend directly on low-level details; instead, low-level details should depend on high level policies. To accomplish this, runtime dependencies can be decoupled from source code dependencies via a shared polymorphic abstraction"

Slide 20

Slide 20 text

interface Logger { fun log(severity: Level, toLog : String) } class SL4JLogger : Logger { override fun log(severity: Level, toLog: String) { // Print to console } } class LogEntriesLogger : Logger { override fun log(severity: Level, toLog: String) { // Send to external logging service } }

Slide 21

Slide 21 text

Dependencies Chain App Controller Logger Instances Factory

Slide 22

Slide 22 text

Dependencies Chain App Controller Instances Factory SL4JLogger Factory.createLogger( )

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

OVERVIEW • 100% Kotlin dependency injection framework • Heavily based on Kotlin features : type system and lazy properties • Reflection-based • Mirrors some API style from Guice, but has its own style as well • Supports Kotlin-JVM, Android, JS and Kotlin-native

Slide 25

Slide 25 text

object Injector { val graph = Kodein.lazy { bind() with singleton { Gson() } bind() with singleton { OkHttpClient.Builder().build() } bind() with provider { RestCaller(httpClient = instance()) } // More bindings ...

Slide 26

Slide 26 text

object Injector { val graph = Kodein.lazy { bind() with singleton { Gson() } bind() with singleton { OkHttpClient.Builder().build() } bind() with provider { RestCaller(httpClient = instance()) } // More bindings ... The inferred LazyKodein subtype represents the object graph

Slide 27

Slide 27 text

object Injector { val graph = Kodein.lazy { bind() with singleton { Gson() } bind() with singleton { OkHttpClient.Builder().build() } bind() with provider { RestCaller(httpClient = instance()) } // More bindings ... Instances will be created in a lazy way

Slide 28

Slide 28 text

object Injector { val graph = Kodein.lazy { bind() with singleton { Gson() } bind() with singleton { OkHttpClient.Builder().build() } bind() with provider { RestCaller(httpClient = instance()) } // More bindings ... Defines a binding, eg, a way to provide an object instance

Slide 29

Slide 29 text

object Injector { val graph = Kodein.lazy { bind() with singleton { Gson() } bind() with singleton { OkHttpClient.Builder().build() } bind() with provider { RestCaller(httpClient = instance()) } // More bindings ... Defines the lifecycle time - or scope - of this object

Slide 30

Slide 30 text

object Injector { val graph = Kodein.lazy { bind() with singleton { Gson() } bind() with singleton { OkHttpClient.Builder().build() } bind() with provider { RestCaller(httpClient = instance()) } // More bindings ... Pulls a dependency transitively from the graph, with a proper scope

Slide 31

Slide 31 text

bind() with provider { APIGateway( homeService = instance(), brokerService = instance(), walletService = instance() ) } You should use named parameters to clarify the dependencies that are being pulled transitively

Slide 32

Slide 32 text

bind(UITHREAD) with singleton { AndroidSchedulers.mainThread() } bind(WORKER) with singleton { Schedulers.io() } bind() with provider { SomeInfrastructure( storage = instance(), webService = instance(), worker = instance(WORKER) ) } companion object { val WORKER = "worker" val UITHREAD = "main" }

Slide 33

Slide 33 text

You can bind multiple instances of same type using tags bind(UITHREAD) with singleton { AndroidSchedulers.mainThread() } bind(WORKER) with singleton { Schedulers.io() } bind() with provider { SomeInfrastructure( storage = instance(), webService = instance(), worker = instance(WORKER) ) } companion object { val WORKER = "worker" val UITHREAD = "main" }

Slide 34

Slide 34 text

bind() with singleton { OkHttpClient.Builder().build() } bind() with provider { RestCaller(httpClient = instance()) }

Slide 35

Slide 35 text

bind() with singleton { OkHttpClient.Builder().build() } bind() with provider { RestCaller(httpClient = instance()) } This dependency will live as long the Kodein instance lives

Slide 36

Slide 36 text

bind() with singleton { OkHttpClient.Builder().build() } bind() with provider { RestCaller(httpClient = instance()) } Each time this dependency will be required, a new instance will be created

Slide 37

Slide 37 text

You may have a dependency bounded with a custom scope, eg, this dependency will live as long the provided object lives bind() with scopedSingleton(androidActivityScope) { Presenter( scheduler = instance(UITHREAD), view = it ) } The object itself is accessible at binding time

Slide 38

Slide 38 text

fun main(args: Array) { val gateway: APIGateway by Injector.graph.instance() gateway.start() }

Slide 39

Slide 39 text

fun main(args: Array) { val gateway: APIGateway by Injector.graph.instance() gateway.start() } Our class holds the LazyKodein property No class reference needed to retrieve an instance from the graph

Slide 40

Slide 40 text

class App : Application(), KodeinAware { override val kodein by Injection(context = this).graph } class SomeActivity : AppCompatActivity(), SomeView { private val kodein by lazy { LazyKodein(appKodein) } private val presenter by kodein.with(this).instance() // Do your work … }

Slide 41

Slide 41 text

class App : Application(), KodeinAware { override val kodein by Injection(context = this).graph } class SomeActivity : AppCompatActivity(), SomeView { private val kodein by lazy { LazyKodein(appKodein) } private val presenter by kodein.with(this).instance() // Do your work … } This interface defines an instance that can hold a dependency graph

Slide 42

Slide 42 text

class App : Application(), KodeinAware { override val kodein by Injection(context = this).graph } class SomeActivity : AppCompatActivity(), SomeView { private val kodein by lazy { LazyKodein(appKodein) } private val presenter by kodein.with(this).instance() // Do your work … } Provided by the Android support

Slide 43

Slide 43 text

class App : Application(), KodeinAware { override val kodein by Injection(context = this).graph } class SomeActivity : AppCompatActivity(), SomeView { private val kodein by lazy { LazyKodein(appKodein) } private val presenter by kodein.with(this).instance() // Do your work … } Provides the custom dependency scope

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

MUCH MORE • More ways to provide and retrieve dependencies • More ways to define dependencies scopes • Modules support, including dependency overriding • Multibindings support • JSR330 inter-operability for injections • Great documentation

Slide 46

Slide 46 text

FINAL
 REMARKS

Slide 47

Slide 47 text

CONCLUSIONS • Kodein is a great Kotlin first-class DI framework • You dont have to follow any JSR330 conventions to define the object graph • Kodein has same pain-points that reflection-based DI framework have : not so fast when compared with reflection-free solutions, errors may occur at runtime due to dependencies resolution failures • Great alternative for DI on Android applications

Slide 48

Slide 48 text

https://speakerdeck.com/ubiratansoares

Slide 49

Slide 49 text

UBIRATAN SOARES Computer Scientist by ICMC/USP Software Engineer, curious guy Google Developer Expert for Android Teacher, speaker, etc, etc

Slide 50

Slide 50 text

THANK YOU @ubiratanfsoares ubiratansoares.github.io https://br.linkedin.com/in/ubiratanfsoares