Upgrade to Pro — share decks privately, control downloads, hide ads and more …

To Inject or not inject, Dependency Injection in a Kotlin world (appbuilders.ch 2018)

To Inject or not inject, Dependency Injection in a Kotlin world (appbuilders.ch 2018)

Dependency Injection via frameworks like Dagger were present in most modern Android projects. But then Kotlin happened. And since then a bunch of new libraries like Koin or Kodein appeared. Developers are even writing articles how to do DI without any framework. Some argue that those don’t even offer real dependency injection. Let’s look at that argument and compare the approaches. So, is there something wrong with Dagger & co. in Kotlin? Are they obsolete? What are the alternatives? Let’s dive in.

Danny Preussler

April 16, 2018
Tweet

More Decks by Danny Preussler

Other Decks in Programming

Transcript

  1. To Inject or not inject Dependency injection in a Kotlin

    world @PreusslerBerlin flickr.com/photos/51035610542@N01/6254235315
  2. @PreusslerBerlin 1994 Design Patterns Late 90s Age of Factories Early

    2000 J2EE, Java 1.3 Service Locators 2003 Spring 2007 Guice 2010 JSR 330 2011 RoboGuice 2013 Dagger 2015 Dagger 2 2017 Dagger Android • It started with the new keyword: new Player(….) Nothing says “implementation” more than the new keyword flickr.com/photos/139228535@N05/40102368982
  3. @PreusslerBerlin 1994 Design Patterns Late 90s Age of Factories Early

    2000 J2EE, Java 1.3 Service Locators 2003 Spring 2007 Guice 2010 JSR 330 2011 RoboGuice 2013 Dagger 2015 Dagger 2 2017 Dagger Android • Singleton Pattern • Factory Pattern • Abstract Factory Pattern • Prototype Pattern • Builder Pattern flickr.com/photos/139228535@N05/40102368982
  4. @PreusslerBerlin 1994 Design Patterns Late 90s Age of Factories Early

    2000 J2EE, Java 1.3 Service Locators 2003 Spring 2007 Guice 2010 JSR 330 2011 RoboGuice 2013 Dagger 2015 Dagger 2 2017 Dagger Android • Factories everywhere flickr.com/photos/139228535@N05/40102368982
  5. @PreusslerBerlin 1994 Design Patterns Late 90s Age of Factories Early

    2000 J2EE, Java 1.3 Service Locators 2003 Spring 2007 Guice 2010 JSR 330 2011 RoboGuice 2013 Dagger 2015 Dagger 2 2017 Dagger Android • Context, Locator, Registers • Java1.3 had Serviceloader buildin in jar spec • The Godzilla Factory Context.getInstance().getCollaboratorY().getCollaboratorZ(); • Hive, Avalon, Tapestry IOC, Pico. (often based on Interfaces) flickr.com/photos/139228535@N05/40102368982
  6. @PreusslerBerlin 1994 Design Patterns Late 90s Age of Factories Early

    2000 J2EE, Java 1.3 Service Locators 2003 Spring 2007 Guice 2010 JSR 330 2011 RoboGuice 2013 Dagger 2015 Dagger 2 2017 Dagger Android • The Dawn of Dependency Injection • Idea: register the objects and let them handle the creation • Started with xml configuration • With JDK 1.5 came annotation support @AutoWired flickr.com/photos/139228535@N05/40102368982
  7. @PreusslerBerlin 1994 Design Patterns Late 90s Age of Factories Early

    2000 J2EE, Java 1.3 Service Locators 2003 Spring 2007 Guice 2010 JSR 330 2011 RoboGuice 2013 Dagger 2015 Dagger 2 2017 Dagger Android • Guice came • Spring joined with configuration via code „ JavaConfig“ flickr.com/photos/139228535@N05/40102368982
  8. @PreusslerBerlin 1994 Design Patterns Late 90s Age of Factories Early

    2000 J2EE, Java 1.3 Service Locators 2003 Spring 2007 Guice 2010 JSR 330 2011 RoboGuice 2013 Dagger 2015 Dagger 2 2017 Dagger Android • 2010 annotations standardized: javax.inject flickr.com/photos/139228535@N05/40102368982
  9. @PreusslerBerlin 1994 Design Patterns Late 90s Age of Factories Early

    2000 J2EE, Java 1.3 Service Locators 2003 Spring 2007 Guice 2010 JSR 330 2011 RoboGuice 2013 Dagger 2015 Dagger 2 2017 Dagger Android • RoboGuice started by community • Reflection based Google: don‘t use it flickr.com/photos/139228535@N05/40102368982
  10. @PreusslerBerlin 1994 Design Patterns Late 90s Age of Factories Early

    2000 J2EE, Java 1.3 Service Locators 2003 Spring 2007 Guice 2010 JSR 330 2011 RoboGuice 2013 Dagger 2015 Dagger 2 2017 Dagger Android • Dagger1 by Square • „similar“ to Guice • Pro: moved from Reflection to compile time • Contra: Graph composition still at runtime, Ugly genereated code flickr.com/photos/139228535@N05/40102368982
  11. @PreusslerBerlin 1994 Design Patterns Late 90s Age of Factories Early

    2000 J2EE, Java 1.3 Service Locators 2003 Spring 2007 Guice 2010 JSR 330 2011 RoboGuice 2013 Dagger 2015 Dagger 2 2017 Dagger Android • Dagger2 by Google • Fixed the drawbacks • Complete compile time verified flickr.com/photos/139228535@N05/40102368982
  12. @PreusslerBerlin 1994 Design Patterns Late 90s Age of Factories Early

    2000 J2EE, Java 1.3 Service Locators 2003 Spring 2007 Guice 2010 JSR 330 2011 RoboGuice 2013 Dagger 2015 Dagger 2 2017 Dagger Android • Android extensions for Dagger flickr.com/photos/139228535@N05/40102368982
  13. @PreusslerBerlin • Still the way to go? • What about

    build time impact? @PreusslerBerlin
  14. @PreusslerBerlin The problems: • Annotations? • Too much magic •

    Dagger needs a lot of boilerplate • Back to simplicity • Limitations of Java as language @PreusslerBerlin
  15. @PreusslerBerlin How can Kotlin help? • Lazy • Delegates •

    Type inference • Inlining (generic T) • Lambdas • Functional programming @PreusslerBerlin
  16. @PreusslerBerlin @Inject does not see default arguments: class PlayerViewModel( val

    schedulers: RxSchedulers = DefaultSchedulers()) vs class PlayerViewModel( val schedulers: RxSchedulers) @Inject constructor(): this(DefaultSchedulers())
  17. @PreusslerBerlin @Inject does not see default arguments: class PlayerViewModel( val

    schedulers: RxSchedulers = DefaultSchedulers()) vs class PlayerViewModel( val schedulers: RxSchedulers) @Inject constructor(): this(DefaultSchedulers())
  18. @PreusslerBerlin @Inject does not see default arguments: class PlayerViewModel( val

    schedulers: RxSchedulers = DefaultSchedulers()) vs class PlayerViewModel( val schedulers: RxSchedulers) @Inject constructor(): this(DefaultSchedulers())
  19. @PreusslerBerlin Examples (Koin) factory{ DummyApi() as API } bean {

    ViewDataBindingFactory() as BinderFactory }
  20. @PreusslerBerlin The annotated contructor issue • @Inject to mark constructor

    is handy: class HomeViewModel @Inject constructor(val resources: Resources) • Violates separations of concerns • Violates clean architecture : DI should stay in outermost layer • Providers are the cleaner solution anyway (part of JSR330)
  21. @PreusslerBerlin The annotated contructor issue • @Inject to mark constructor

    is handy: class HomeViewModel @Inject constructor(val resources: Resources) • Violates separations of concerns • Violates clean architecture : DI should stay in outermost layer • Providers are the cleaner solution anyway (part of JSR330)
  22. @PreusslerBerlin Service Locator public static MovieFinder movieFinder() { return soleInstance.movieFinder;

    } ... private void configure() { ServiceLocator.load( new ServiceLocator( new ColonMovieFinder("movies1.txt"))); }
  23. @PreusslerBerlin Martin says: • both provide fundamental decoupling • In

    both: application code is independent of the concrete implementation
  24. @PreusslerBerlin Martin says: important difference is about how implementation is

    provided • With service locator: the class asks for it • With injection there is no explicit request • With a Service Locator every user has a dependency to the locator
  25. @PreusslerBerlin Martin says: So the decision between locator and injector

    depends on whether that dependency is a problem.
  26. @PreusslerBerlin Martin says: DI… comes at a price too: •

    It tends to be hard to understand, problems when you are trying to debug.
  27. @PreusslerBerlin • Are Service Locators NOT Dependency Injection! • Kodein

    is a Dependency Retrieval container … The main differences with a Service Locator are the binding scopes (singleton, multiton, provider, etc.) „ Salomon Brys, Kodein author &
  28. @PreusslerBerlin The issue is android • Normally constructor injection preferred

    • On Android: framework classes need field/setter injection: Activity, Service, Fragment, Views
  29. @PreusslerBerlin Deeper look at field/setter injection val binder: BinderFactory by

    inject() It depends? We can decide when looking behind the delegate But isn’t that the same as Daggers generated code?
  30. @PreusslerBerlin Jake admits: • Ultimately Dagger is: • a service

    locator that forces requests be public API • moves validation to compile-time • and lookup is done by code generation • For Service Locators: • Dependencies are no longer part of the API contract. https://twitter.com/JakeWharton/status/908423738244427776
  31. @PreusslerBerlin So what‘s the deal? • Performance comparable • Compile-time

    validation for completeness • Code generates the boilerplate for manual DI • But: Dagger has more boilerplate than all of those? And through reified Koin reduced this
  32. @PreusslerBerlin So what‘s the deal? • Performance comparable • Compile-time

    validation for completeness • Code generates the boilerplate for manual DI • But: Dagger has more boilerplate than all of those? And through reified Koin reduced this
  33. @PreusslerBerlin Boilerplate • Manual dependency injection has zero fixed cost

    …scales poorly. • A service locator has some overhead in creating it, inserting the bindings, and then doing the lookups. • A dependency injector has a lot of fixed overhead in setting up an injector hierarchy (components in Dagger 2) and all the modules to provide bindings. https://www.reddit.com/r/androiddev/comments/8ch4cg/dagger2_vs_koin_for_dependency_injection/
  34. @PreusslerBerlin Boilerplate • Manual dependency injection has zero fixed cost

    …scales poorly. • A service locator has some overhead in creating it, inserting the bindings, and then doing the lookups. • A dependency injector has a lot of fixed overhead in setting up an injector hierarchy (components in Dagger 2) and all the modules to provide bindings. https://www.reddit.com/r/androiddev/comments/8ch4cg/dagger2_vs_koin_for_dependency_injection/
  35. @PreusslerBerlin Boilerplate • Manual dependency injection has zero fixed cost

    …scales poorly. • A service locator has some overhead in creating it, inserting the bindings, and then doing the lookups. • A dependency injector has a lot of fixed overhead in setting up an injector hierarchy (components in Dagger 2) and all the modules to provide bindings. https://www.reddit.com/r/androiddev/comments/8ch4cg/dagger2_vs_koin_for_dependency_injection/
  36. @PreusslerBerlin Boilerplate • if you're going to write a serious

    app with hundreds of bindings and hundreds of injected types with a deep graph of types then you're better off with a proper injector that generates the code that you otherwise manually write worth Koin. https://www.reddit.com/r/androiddev/comments/8ch4cg/dagger2_vs_koin_for_dependency_injection/ (only true when using @ constructor)
  37. @PreusslerBerlin Compile time validation • Dagger abandon the runtime configuration

    idea of DI • Dagger dismissed the idea of being able to deploy without config • Java is the only island that uses compile time verification* • Is this the right path?
  38. @PreusslerBerlin Compile time validation • Dagger abandon the runtime configuration

    idea of DI • Dagger dismissed the idea of being able to deploy without config • Java is the only island that uses compile time verification* • Is this the right path?
  39. @PreusslerBerlin Is this the right path? • Whose job is

    it to manage the nulls. The language? Or the programmer? • Defects are the fault of programmers. It is programmers who create defects – not languages. • And what is it that programmers are supposed to do to prevent defects? • TEST! flickr.com/photos/steve-maw/4543646311
  40. @PreusslerBerlin Is this the right path? • Whose job is

    it to manage the nulls. The language? Or the programmer? • Defects are the fault of programmers. It is programmers who create defects – not languages. • And what is it that programmers are supposed to do to prevent defects? • TEST! flickr.com/photos/steve-maw/4543646311
  41. @PreusslerBerlin Is this the right path? • Whose job is

    it to manage the nulls. The language? Or the programmer? • Defects are the fault of programmers. It is programmers who create defects – not languages. • And what is it that programmers are supposed to do to prevent defects? • TEST! flickr.com/photos/steve-maw/4543646311
  42. @PreusslerBerlin Is this the right path? • Whose job is

    it to manage the nulls. The language? Or the programmer? • Defects are the fault of programmers. It is programmers who create defects – not languages. • And what is it that programmers are supposed to do to prevent defects? • TEST! https://blog.cleancoder.com/uncle-bob/2017/01/11/TheDarkPath.html flickr.com/photos/steve-maw/4543646311
  43. @PreusslerBerlin Is this the right path? • What about the

    compile time safety for DI? • Why did the nuclear plant at Chernobyl catch fire, melt down, destroy a small city, and leave a large area uninhabitable? They overrode all the safeties. So don’t depend on safeties to prevent catastrophes flickr.com/photos/steve-maw/4543646311
  44. @PreusslerBerlin Is this the right path? • What about the

    compile time safety for DI? • „Why did the nuclear plant at Chernobyl catch fire, melt down, destroy a small city, and leave a large area uninhabitable? They overrode all the safeties. “ • So don’t depend on safeties to prevent catastrophes https://blog.cleancoder.com/uncle-bob/2017/01/11/TheDarkPath.html flickr.com/photos/steve-maw/4543646311
  45. @PreusslerBerlin What about JSR-330 and Kotlin? • A unified dependency

    injection standard for DI frameworks • javax.inject • Spring, Guice, Dagger, Toothpick all comply (partially) flickr.com/photos/abbyladybug/2097312947
  46. @PreusslerBerlin What about JSR-330 and Kotlin? • Where based on

    Java‘s lacks of modern language features • So it does not apply • But we miss a new standard for delegates like „by inject“ • Kodein does support JSR-330 (for migration purpose only) kodein-jxinject module flickr.com/photos/abbyladybug/2097312947
  47. @PreusslerBerlin Things we should learn from past • Don‘t build

    Godzilla factories • Law of Demeter • Keep configuration separate of code flickr.com/photos/-pea-/1172413560
  48. @PreusslerBerlin Sum up • „the decision between locator and injector

    depends on whether that dependency is a problem“ • We are not deploying the app without configuration on mobile • Use constructor injection where possible • Don‘t forget the rules of software engineering: Don‘t have one big service locator! • Have tests
  49. @PreusslerBerlin Transition • How easy it is depends on how

    well you hide the DI in your project flickr.com/photos/anca_bejinaru/5234037866/
  50. @PreusslerBerlin Transition • How easy it is depends on how

    well you hide the DI in your project • Painful remove all @inject constructors and replace with manual calls flickr.com/photos/anca_bejinaru/5234037866/
  51. @PreusslerBerlin Transition • How easy it is depends on how

    well you hide the DI in your project • Painful remove all @inject constructors and replace with manual calls • Might end up in awkward readable syntax: factory { HomeViewModel(get(), get(), get(), get(), get(), get()) } flickr.com/photos/anca_bejinaru/5234037866/ giphy.com
  52. @PreusslerBerlin Transition • How easy it is depends on how

    well you hide the DI in your project • Painful remove all @inject constructors and replace with manual calls • Might end up in awkward readable syntax • Kodein helps with JSR330 implementation for short term flickr.com/photos/anca_bejinaru/5234037866/
  53. @PreusslerBerlin Features you might want to have • Tags/Names •

    Scoping • Overriding / testing • Android module • Only supports interfaces? • Multi modules support! Koin ✓ Kodein ✓
  54. @PreusslerBerlin Read more • http://mvpjava.com/brief-history-dependency-injection • http://www.pacoworks.com/2018/03/11/simple-dependency- injection-in-kotlin-part-2/ • https://android.jlelse.eu/moving-from-dagger-to-koin-simplify-your-

    android-development-e8c61d80cddb • http://mvpjava.com/brief-history-dependency-injection/ • https://medium.com/@JorgeCastilloPr/kotlin-dependency-injection- with-the-reader-monad-7d52f94a482e • https://blog.gouline.net/kapsule-minimalist-dependency-injection- for-kotlin-ed3e344d60ed flickr.com/photos/139228535@N05/40102368982