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

Dependency Injection from zero to hero with koin

Dependency Injection from zero to hero with koin

Avatar for Teja Narayan Chirala

Teja Narayan Chirala

December 07, 2019
Tweet

Other Decks in Technology

Transcript

  1. Dependency Injection from 0 -> ‍♂ using koin Teja Chirala

    Android Developer @Mindvalley GDG Kuala Lumpur
  2. Kuala Lumpur • 300+ employees • 50+ Nationalities • 40+

    Languages spoken. Top 10 world’s coolest offices Inc. magazine (2012, 2019) • Malaysia’s healthiest employees , 2019 • Malaysia’s healthiest workplace , 2019 • Malaysia’s healthiest employer , 2019
  3. What is a dependency? Kuala Lumpur Printer PaperTray • Class

    needs another class to perform a task. • All apps have dependencies. Dependency
  4. What is a dependency Injection? Kuala Lumpur Printer PaperTray •

    Dependencies are provided / injected to the class instead of the class creating them itself.
  5. Without DI class Printer { private val paperTray: PaperTray =

    PaperTray() } Kuala Lumpur Printer PaperTray
  6. With DI - Constructor Injection class Printer(val paperTray: PaperTray) {

    } fun main(args: Array) { val paperTray = PaperTray() val printer = Printer(paperTray) } Kuala Lumpur Printer PaperTray
  7. With DI - Setter Injection class Printer() { lateinit var

    paperTray: PaperTray } fun main(args: Array) { val printer = Printer() printer.paperTray = PaperTray() } Kuala Lumpur Printer PaperTray
  8. Importance of DI Kuala Lumpur • Helps in achieving Separation

    of concerns of construction and use of objects
  9. Reuse Printer fun main(args: Array) { val a4Printer = Printer(A4SizePaperTray())

    val a1Printer = Printer(A1SizePaperTray()) } Kuala Lumpur
  10. Importance of DI Kuala Lumpur • Helps in achieving Separation

    of concerns of construction and use of objects ◦ Readability ◦ Reusability • Decreases coupling between a object and its dependency
  11. Importance of DI Kuala Lumpur • Helps in achieving Separation

    of concerns of construction and use of objects ◦ Readability ◦ Reusability • Decreases coupling between a object and its dependency • Ease of refactoring
  12. class Printer( private val paperTray: PaperTray, private val scanner: Scanner,

    private val paperRoller: PaperRoller, private val ledInteractor: LEDInteractor ) { //small source code } Kuala Lumpur
  13. Importance of DI Kuala Lumpur • Helps in achieving Separation

    of concerns of construction and use of objects ◦ Readability ◦ Reusability • Decreases coupling between a object and its dependency • Ease of refactoring ◦ Smaller foused classes • Easy tests
  14. class PrinterTest { @Test fun `printer happy path`() { val

    printer = Printer(fakePaperBundle()) ... } } Kuala Lumpur
  15. class PrinterTest { @Test fun `printer happy path`() { val

    printer = Printer(fakePaperBundle()) ... } @Test fun `printer empty paper`() { val printer = Printer(fakeEmptyPaperBundle()) ... } } Kuala Lumpur
  16. interface PaperBundle { fun getPaper() fun setPaperRolled() fun canRollPaper():Boolean }

    Kuala Lumpur class PaperTray: PaperBundle { private var paperCount = 10 private var canRollPaper = false override fun getPaper() { if (paperCount > 0) { println("Getting paper =>=>=>") paperCount-- canRollPaper = true } } override fun setPaperRolled() { canRollPaper = false } override fun canRollPaper() : Boolean { return canRollPaper } }
  17. interface Roller { fun rollPaper() } Kuala Lumpur class PrinterRoller(private

    val paperBundle: PaperBundle): Roller{ override fun rollPaper() { if (paperBundle.canRollPaper()) { println("oooooo rolling oooooo") } } }
  18. class Printer(private val paperBundle: PaperBundle, private val roller: Roller) {

    fun print() { paperBundle.getPaper() println("Printing.....") roller.rollPaper() } } Kuala Lumpur
  19. val paperTray = PaperTray() val printerRoller = PrinterRoller(paperTray) val printer

    = Printer(paperTray, printerRoller) printer.print() Kuala Lumpur Manual DI - Problem?
  20. Why Koin? Kuala Lumpur • Simple to read and understand

    • Meaningful • Not driven by theory alone • No over engineering
  21. //declare dependencies here module { //definition Single | factory {

    instance creation expression } } Kuala Lumpur
  22. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { } Kuala Lumpur
  23. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { } Kuala Lumpur
  24. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { single { PaperTray() } } Kuala Lumpur
  25. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { single<PaperBundle> { PaperTray() } } Kuala Lumpur
  26. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { single<PaperBundle> { PaperTray() } } Kuala Lumpur
  27. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { single<PaperBundle> { PaperTray() } single<Roller> { PrinterRoller() } } Kuala Lumpur
  28. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { single<PaperBundle> { PaperTray() } single<Roller> { PrinterRoller(???) } } Kuala Lumpur
  29. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { single<PaperBundle> { PaperTray() } single<Roller> { PrinterRoller(get<PaperBundle>()) } } Kuala Lumpur
  30. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { single<PaperBundle> { PaperTray() } single<Roller> { PrinterRoller(get()) } } Kuala Lumpur
  31. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { single<PaperBundle> { PaperTray() } single<Roller> { PrinterRoller(get()) } single { Printer(???, ???) } } Kuala Lumpur
  32. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { single<PaperBundle> { PaperTray() } single<Roller> { PrinterRoller(get()) } single { Printer(get(), get()) } } Kuala Lumpur
  33. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { single<PaperBundle> { PaperTray() } single<Roller> { PrinterRoller(get()) } single { Printer(get(), get()) } } Kuala Lumpur
  34. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { single { Printer(get(), get()) } } val printerComponentsModule = module { single<PaperBundle> { PaperTray() } single<Roller> { PrinterRoller(get()) } } Kuala Lumpur
  35. Start Koin class MainApplication : Application() { override fun onCreate()

    { super.onCreate() startKoin { } } } Kuala Lumpur
  36. Start Koin class MainApplication : Application() { override fun onCreate()

    { super.onCreate() startKoin { // Koin Android logger androidLogger() } } } Kuala Lumpur
  37. Start Koin class MainApplication : Application() { override fun onCreate()

    { super.onCreate() startKoin { // Koin Android logger androidLogger() //inject Android context androidContext(this@MainApplication) } } } Kuala Lumpur
  38. Start Koin class MainApplication : Application() { override fun onCreate()

    { super.onCreate() startKoin { // Koin Android logger androidLogger() //inject Android context androidContext(this@MainApplication) // use modules modules(printerModule) } } } Kuala Lumpur
  39. Start Koin class MainApplication : Application() { override fun onCreate()

    { super.onCreate() startKoin { // Koin Android logger androidLogger() //inject Android context androidContext(this@MainApplication) // use modules modules(listOf(printerModule, printerComponentsModule)) } } } Kuala Lumpur
  40. //Getting the dependencies class MainActivity : AppCompatActivity() { override fun

    onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... } } Kuala Lumpur
  41. //Getting the dependencies class MainActivity : AppCompatActivity() { // Eagerly

    injected Printer instance val printer : Printer = get() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... } } Kuala Lumpur
  42. //Getting the dependencies class MainActivity : AppCompatActivity() { // Lazy

    injected Printer instance val printer : Printer by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... } } Kuala Lumpur
  43. //Getting the dependencies class MainActivity : AppCompatActivity() { // Lazy

    injected Printer instance val printer : Printer by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) printer.print() } } Kuala Lumpur
  44. Koin component is an interface marker Identify class linked to

    koin API ✅ Open access to koin extensions inject(),get() Kuala Lumpur
  45. class PrinterApp: KoinComponent { private val printer: Printer by inject()

    fun startPrinting() { printer.print() } } Kuala Lumpur
  46. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { single<PaperBundle> { PaperTray() } single<Roller> { PrinterRoller(get()) } single { Printer(get(), get()) } } Kuala Lumpur
  47. class PaperTray: PaperBundle class PrinterRoller(private val paperBundle: PaperBundle): Roller class

    Printer(private val paperBundle: PaperBundle, private val roller: Roller) val printerModule = module { single<PaperBundle> { PaperTray() } single<Roller> { PrinterRoller(get()) } single { Printer(lazy {get<PaperBundle>()}, get()) } } Kuala Lumpur
  48. class Printer(private val paperBundle: PaperBundle, private val roller: Roller) {

    fun print() { paperBundle.getPaper() println("Printing.....") roller.rollPaper() } } Kuala Lumpur
  49. class Printer(_paperBundle: Lazy<PaperBundle>, private val roller: Roller) { private val

    paperBundle by _paperBundle fun print() { paperBundle.getPaper() println("Printing.....") roller.rollPaper() } } * Lazy is Kotlin type Kuala Lumpur
  50. class Printer(_paperBundle: Lazy<PaperBundle>, private val roller: Roller) { private val

    paperBundle by _paperBundle fun print() { paperBundle.getPaper() println("Printing.....") roller.rollPaper() } } Kuala Lumpur
  51. @Module(includes = [PaperModule::class]) class RollerModule { @Singleton @Provides fun providesRoller(paperBundle:

    PaperBundle) : Roller { return PrinterRoller(paperBundle) } } Kuala Lumpur
  52. class Printer @Inject constructor(_paperBundle: Lazy<PaperBundle>, private val roller: Roller) {

    private val paperBundle by lazy { _paperBundle.get() } fun print() { paperBundle.getPaper() println("Printing.....") roller.rollPaper() } } * Lazy is dagger type Kuala Lumpur
  53. class Printer @Inject constructor(_paperBundle: Lazy<PaperBundle>, private val roller: Roller) {

    private val paperBundle by lazy { _paperBundle.get() } fun print() { paperBundle.getPaper() println("Printing.....") roller.rollPaper() } } Kuala Lumpur
  54. @Module(includes = [RollerModule::class]) open class PrinterModule { @Singleton @Provides open

    fun providePrinter(paperBundle: PaperBundle, roller: Roller) : Printer { return Printer( Lazy {paperBundle} , roller) } } Kuala Lumpur
  55. class ApplicationClass: Application() { override fun onCreate() { super.onCreate() val

    printerComponent = DaggerPrinterComponent.builder() .printerModule(PrinterModule()) .build() printerComponent.printer().print() } } Kuala Lumpur
  56. class PrinterRoller @Inject constructor(private val paperBundle: PaperBundle): Roller { override

    fun rollPaper() { if (paperBundle.isPaperAvailable()) { println("oooooo rolling oooooo") } } } Kuala Lumpur
  57. class PrinterRoller (private val paperBundle: PaperBundle): Roller { override fun

    rollPaper() { if (paperBundle.isPaperAvailable()) { println("oooooo rolling oooooo") } } } Kuala Lumpur
  58. class PrinterRoller : Roller { private val paperBundle: PaperBundle override

    fun rollPaper() { if (paperBundle.isPaperAvailable()) { println("oooooo rolling oooooo") } } } Kuala Lumpur
  59. class PrinterRoller : Roller, KoinComponent { private val paperBundle: PaperBundle

    by inject() override fun rollPaper() { if (paperBundle.isPaperAvailable()) { println("oooooo rolling oooooo") } } } Kuala Lumpur
  60. class Printer @Inject constructor(_paperBundle: Lazy<PaperBundle>, private val roller: Roller) {

    private val paperBundle by lazy { _paperBundle.get() } fun print() { paperBundle.getPaper() println("Printing.....") roller.rollPaper() } } Kuala Lumpur
  61. class Printer @Inject constructor(_paperBundle: Lazy<PaperBundle>, private val roller: Roller) {

    private val paperBundle by lazy { _paperBundle.get() } fun print() { paperBundle.getPaper() println("Printing.....") roller.rollPaper() } } Kuala Lumpur
  62. class Printer @Inject constructor(private val roller: Roller) { private val

    paperBundle: PaperBundle by inject() fun print() { paperBundle.getPaper() println("Printing.....") roller.rollPaper() } } Kuala Lumpur
  63. What is a Scope? A scope is context with fixed

    duration of time, in which an object exists. When the scope ends, any object bound under the scope cannot be injected again. Kuala Lumpur
  64. dependencies { def koinVersion = "2.0.1" // koin for android

    + scope feature implementation "org.koin:koin-android-scope:$koinVersion" } Kuala Lumpur
  65. Scope scope(named("session")) { scoped { UserSession() } } Kuala Lumpur

    //create a session with id `ourSession` and qualifier `session` val ourSession = getKoin().createScope("ourSession",named("session")) //get session instance from scope val userSession : UserSession by ourSession.inject() //closing the scope ourSession.close()
  66. Current scope val androidModule = module { scope(named<MyActivity>()) { scoped

    { Presenter() } } } Kuala Lumpur class MyActivity : AppCompatActivity() { // inject Presenter instance from current scope val presenter : Presenter by currentScope.inject()
  67. dependencies { def koinVersion = "2.0.1" // koin for android

    + viewmodel feature implementation "org.koin:koin-android-viewmodel:$koinVersion" } Kuala Lumpur
  68. View model class MyViewModel(val printer: Printer) : ViewModel() val viewModelModule

    = module { viewModel { MyViewModel(???) } } Kuala Lumpur
  69. View model class MyViewModel(val printer: Printer) : ViewModel() val viewModelModule

    = module { viewModel { MyViewModel(get()) } } Kuala Lumpur
  70. View model class DetailActivity : AppCompatActivity() { // Lazy inject

    ViewModel val detailViewModel: DetailViewModel by viewModel() // Eager inject ViewModel val detailViewModel: DetailViewModel = getViewModel() } Kuala Lumpur
  71. Shared View model val viewModelModule = module { // PrinterViewModel

    declaration for Weather View components viewModel { PrinterViewModel(get(), get()) } } Kuala Lumpur
  72. Shared View model class PrinterActivity : AppCompatActivity() { private val

    printerViewModel by viewModel<PrinterViewModel>() } class PrinterHeaderFragment : Fragment() { private val weatherViewModel by sharedViewModel<PrinterViewModel>() } class PrinterListFragment : Fragment() { private val weatherViewModel by sharedViewModel<PrinterViewModel>() } Kuala Lumpur
  73. dependencies { def koinVersion = "2.0.1" // koin for tests

    testImplementation "org.koin:koin-test:$koinVersion" } Kuala Lumpur
  74. class KoinTest { @Test fun `check all definitions of printer

    module`() { startKoin { } } } Kuala Lumpur
  75. class KoinTest { @Test fun `check all definitions of printer

    module`() { startKoin { modules(printerModule) } } } Kuala Lumpur
  76. class KoinTest { @Test fun `check all definitions of printer

    module`() { startKoin { modules(printerModule) }.checkModules() } } Kuala Lumpur
  77. class PrinterTest: KoinTest { @Test fun `check roll paper status

    before and after print`() { startKoin { modules(printerModule) } } } Kuala Lumpur
  78. class PrinterTest: KoinTest { private val printer: Printer by inject()

    private val paperBundle: PaperBundle by inject() @Test fun `check roll paper status before and after print`() { startKoin { modules(printerModule) } } } Kuala Lumpur
  79. class PrinterTest: KoinTest { private val printer: Printer by inject()

    private val paperBundle: PaperBundle by inject() @Test fun `check roll paper status before and after print`() { startKoin { modules(printerModule) } declareMock<PaperBundle>() } } Kuala Lumpur
  80. class PrinterTest: KoinTest { private val printer: Printer by inject()

    private val paperBundle: PaperBundle by inject() @Test fun `check roll paper status before and after print`() { startKoin { modules(printerModule) } declareMock<PaperBundle>() given(paperBundle.canRollPaper()).will { true } printer.print() } } Kuala Lumpur
  81. class PrinterTest: KoinTest { private val printer: Printer by inject()

    private val paperBundle: PaperBundle by inject() @Test fun `check roll paper status before and after print`() { startKoin { modules(printerModule) } declareMock<PaperBundle>() given(paperBundle.canRollPaper()).will { true } printer.print() verify(paperBundle, times(1)).canRollPaper() verify(paperBundle, times(1)).setPaperRolled() } } Kuala Lumpur
  82. class PrinterTest: KoinTest { private val printer: Printer by inject()

    private val paperBundle: PaperBundle by inject() @Test fun `check roll paper status before and after print`() { startKoin { modules(printerModule) } declareMock<PaperBundle>() given(paperBundle.canRollPaper()).will { true } printer.print() verify(paperBundle, times(1)).canRollPaper() verify(paperBundle, times(1)).setPaperRolled() } } Kuala Lumpur