$30 off During Our Annual Pro Sale. View Details »

The Imposter's Guide To Dependency Injection - DCSF22

Adam McNeilly
June 02, 2022
270

The Imposter's Guide To Dependency Injection - DCSF22

Adam McNeilly

June 02, 2022
Tweet

Transcript

  1. The Imposter's Guide To
    Dependency Injec6on
    Adam McNeilly - @AdamMc331
    @AdamMc331
    #DCSF22 1

    View Slide

  2. Dependency Injec+on
    @AdamMc331
    #DCSF22 2

    View Slide

  3. Dependency Injec+on
    • Two big words
    @AdamMc331
    #DCSF22 2

    View Slide

  4. Dependency Injec+on
    • Two big words
    • Half a dozen libraries
    @AdamMc331
    #DCSF22 2

    View Slide

  5. Dependency Injec+on
    • Two big words
    • Half a dozen libraries
    • Countless Twi6er wars
    @AdamMc331
    #DCSF22 2

    View Slide

  6. Dependency Injec+on
    • Two big words
    • Half a dozen libraries
    • Countless Twi6er wars
    • Endless Confusion
    @AdamMc331
    #DCSF22 2

    View Slide

  7. A Note About Imposter Syndrome1
    1 h$ps:/
    /hbr.org/2021/02/stop-telling-women-they-have-imposter-syndrome

    View Slide

  8. Let's Start At The Beginning
    @AdamMc331
    #DCSF22 4

    View Slide

  9. Code Without DI
    In the following snippet, our class relies directly on
    FirebaseAnalytics.
    class ProfileViewModel() {
    fun onProfileLoaded() {
    Firebase.analytics.logEvent("viewed_profile")
    }
    }
    @AdamMc331
    #DCSF22 5

    View Slide

  10. What Problems Can This Cause?
    @AdamMc331
    #DCSF22 6

    View Slide

  11. Try Tes'ng It
    class ProfileViewModelTest {
    @Test
    fun verifyEventTracked() {
    val viewModel = ProfileViewModel()
    viewModel.onProfileLoaded()
    // No Ability To Verify Event Tracked
    }
    }
    @AdamMc331
    #DCSF22 7

    View Slide

  12. It Also Crashes
    Caused by: java.lang.Run2meExcep2on: Method getMainLooper in
    android.os.Looper not mocked.
    @AdamMc331
    #DCSF22 8

    View Slide

  13. We Have A Dependency On
    FirebaseAnaly1cs, Let's Inject It
    @AdamMc331
    #DCSF22 9

    View Slide

  14. Constructor Injec-on
    class ProfileViewModel(
    private val analytics: FirebaseAnalytics = Firebase.analytics,
    ) {
    fun onProfileLoaded() {
    analytics.logEvent("viewed_profile")
    }
    }
    @AdamMc331
    #DCSF22 10

    View Slide

  15. Constructor Injec-on2
    2 h$ps:/
    /twi$er.com/KellyShuster/status/1020363593060093952
    @AdamMc331
    #DCSF22 11

    View Slide

  16. Updated Test
    class ProfileViewModelTest {
    @Test
    fun verifyEventTracked() {
    val mockAnalytics = FakeFirebaseAnalytics()
    val viewModel = ProfileViewModel(mockAnalytics)
    viewModel.onProfileLoaded()
    mockAnalytics.verifyEventLogged("viewed_profile")
    }
    }
    @AdamMc331
    #DCSF22 12

    View Slide

  17. Wait, Should We Mock Firebase?
    @AdamMc331
    #DCSF22 13

    View Slide

  18. Wait, Should We Mock Firebase?
    • We might change vendors in the future
    @AdamMc331
    #DCSF22 13

    View Slide

  19. Wait, Should We Mock Firebase?
    • We might change vendors in the future
    • Is it Firebase we're tes9ng, or just that some event was tracked?
    @AdamMc331
    #DCSF22 13

    View Slide

  20. We Should Create Our Own Interface
    interface AnalyticsTracker {
    fun trackEvent(eventName: String)
    }
    @AdamMc331
    #DCSF22 14

    View Slide

  21. With Our Own Implementa1on
    class FirebaseAnalyticsTracker : AnalyticsTracker {
    override fun trackEvent(eventName: String) {
    Firebase.analytics.logEvent(eventName)
    }
    }
    @AdamMc331
    #DCSF22 15

    View Slide

  22. Inject That Instead
    class ProfileViewModel(
    private val analytics: AnalyticsTracker = FirebaseAnalyticsTracker(),
    )
    @AdamMc331
    #DCSF22 16

    View Slide

  23. That's The Core Concept Of
    Dependency Injec6on!
    @AdamMc331
    #DCSF22 17

    View Slide

  24. Check Out More3
    3 h$ps:/
    /speakerdeck.com/stkent/framework-free-dependency-injec:on-360-andev-2019
    @AdamMc331
    #DCSF22 18

    View Slide

  25. What Makes It More Complicated
    Than "Passing Stuff In"?
    @AdamMc331
    #DCSF22 19

    View Slide

  26. Sharing Dependencies
    @AdamMc331
    #DCSF22 20

    View Slide

  27. Analy&cs Are Everywhere
    @AdamMc331
    #DCSF22 21

    View Slide

  28. Analy&cs Are Everywhere
    • Let's create one AnalyticsTracker to use in each screen
    @AdamMc331
    #DCSF22 21

    View Slide

  29. Analy&cs Are Everywhere
    • Let's create one AnalyticsTracker to use in each screen
    • Let's ensure that we can swap out that tracker once and have
    each screen updated
    @AdamMc331
    #DCSF22 21

    View Slide

  30. Create A Dependency Container4
    class AppDependencies {
    val analyticsTracker: AnalyticsTracker
    get() = FirebaseAnalyticsTracker()
    }
    4 h$ps:/
    /developer.android.com/training/dependency-injec:on/manual
    @AdamMc331
    #DCSF22 22

    View Slide

  31. Instan&ate In Applica&on Class
    class MyApp : Application() {
    val appDependencies = AppDependencies()
    }
    @AdamMc331
    #DCSF22 23

    View Slide

  32. Fetch Dependencies From Screens
    class ProfileActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val appDependencies = (this.application as MyApp).appDependencies
    val analyticsTracker = appDependencies.analyticsTracker
    val profileViewModel = ProfileViewModel(analyticsTracker)
    }
    }
    @AdamMc331
    #DCSF22 24

    View Slide

  33. This Is The Manual Approach
    @AdamMc331
    #DCSF22 25

    View Slide

  34. DI Libraries Help Reduce Boilerplate
    From The Manual Approach
    @AdamMc331
    #DCSF22 26

    View Slide

  35. They All Follow The Same Recipe
    @AdamMc331
    #DCSF22 27

    View Slide

  36. They All Follow The Same Recipe
    • Create a collec+on of dependencies
    @AdamMc331
    #DCSF22 27

    View Slide

  37. They All Follow The Same Recipe
    • Create a collec+on of dependencies
    • Configure your Applica+on class as the container for those
    dependencies
    @AdamMc331
    #DCSF22 27

    View Slide

  38. They All Follow The Same Recipe
    • Create a collec+on of dependencies
    • Configure your Applica+on class as the container for those
    dependencies
    • Request dependencies as needed
    @AdamMc331
    #DCSF22 27

    View Slide

  39. Hilt Example5
    5 h$ps:/
    /developer.android.com/training/dependency-injec:on/hilt-android
    @AdamMc331
    #DCSF22 28

    View Slide

  40. Dependency Container
    @Module
    @InstallIn(ActivityComponent::class)
    abstract class AnalyticsModule {
    @Binds
    abstract fun bindAnalyticsTracker(
    analyticsTracker: FirebaseAnalyticsTracker,
    ): AnalyticsTracker
    }
    @AdamMc331
    #DCSF22 29

    View Slide

  41. Dependency Container
    @Module
    @InstallIn(ActivityComponent::class)
    abstract class AnalyticsModule {
    @Binds
    abstract fun bindAnalyticsTracker(
    analyticsTracker: FirebaseAnalyticsTracker,
    ): AnalyticsTracker
    }
    @AdamMc331
    #DCSF22 29

    View Slide

  42. Applica'on Class
    @HiltAndroidApp
    class MyApp : Application()
    @AdamMc331
    #DCSF22 30

    View Slide

  43. Request Dependencies
    @AndroidEntryPoint
    class ProfileActivity : Activity() {
    @Inject
    lateinit var analyticsTracker: AnalyticsTracker
    }
    @AdamMc331
    #DCSF22 31

    View Slide

  44. Koin Example6
    6 h$ps:/
    /insert-koin.io/docs/quickstart/android/
    @AdamMc331
    #DCSF22 32

    View Slide

  45. Dependency Container
    val analyticsModule = module {
    single {
    FirebaseAnalyticsTracker()
    }
    }
    @AdamMc331
    #DCSF22 33

    View Slide

  46. Dependency Container
    val analyticsModule = module {
    single {
    FirebaseAnalyticsTracker()
    }
    }
    @AdamMc331
    #DCSF22 33

    View Slide

  47. Applica'on Class
    class MyApp : Application() {
    override fun onCreate() {
    super.onCreate()
    startKoin {
    modules(analyticsModule)
    }
    }
    }
    @AdamMc331
    #DCSF22 34

    View Slide

  48. Request Dependencies
    class ProfileActivity : Activity() {
    val analyticsTracker: AnalyticsTracker by inject()
    }
    @AdamMc331
    #DCSF22 35

    View Slide

  49. Your DI Library Of Choice Ma4ers
    Less Than Why You Need It
    @AdamMc331
    #DCSF22 36

    View Slide

  50. Dependency Management Enables Several
    Benefits
    @AdamMc331
    #DCSF22 37

    View Slide

  51. Dependency Management Enables Several
    Benefits
    • Unit test support
    @AdamMc331
    #DCSF22 37

    View Slide

  52. Dependency Management Enables Several
    Benefits
    • Unit test support
    • Ability to change dependencies
    @AdamMc331
    #DCSF22 37

    View Slide

  53. Dependency Management Enables Several
    Benefits
    • Unit test support
    • Ability to change dependencies
    • Dependencies can be shared
    @AdamMc331
    #DCSF22 37

    View Slide

  54. Dependency Management Enables Several
    Benefits
    • Unit test support
    • Ability to change dependencies
    • Dependencies can be shared
    • One centralized configura:on of all dependencies
    @AdamMc331
    #DCSF22 37

    View Slide

  55. A Li%le Bit More
    @AdamMc331
    #DCSF22 38

    View Slide

  56. Scoping
    @AdamMc331
    #DCSF22 39

    View Slide

  57. Applica'on Scoped
    Some dependencies should be maintained as a single instance for
    our en5re applica5on lifecycle. This can be true for database
    connec5ons, error reporters, analy5cs trackers, and similar far
    reaching func5onality.
    @AdamMc331
    #DCSF22 40

    View Slide

  58. Component Scoped
    Some dependencies should only be maintained as long as the
    components using them require it. For example, a presenter only
    needs to stay in memory if the screen using it is visible or in the
    backstack, or a cache layer only for a specific screen.
    @AdamMc331
    #DCSF22 41

    View Slide

  59. Library Differences
    How we manage scoping is different depending on the DI
    framework we choose, but in advanced situa;ons we should be
    aware of memory management and ensuring we either share or
    create new dependencies as necessary.
    @AdamMc331
    #DCSF22 42

    View Slide

  60. What To Consider
    @AdamMc331
    #DCSF22 43

    View Slide

  61. Manual Approach
    @AdamMc331
    #DCSF22 44

    View Slide

  62. Manual Approach
    • Pro: No "magic"
    @AdamMc331
    #DCSF22 44

    View Slide

  63. Manual Approach
    • Pro: No "magic"
    • Pro: Ability to use "find usages"
    @AdamMc331
    #DCSF22 44

    View Slide

  64. Manual Approach
    • Pro: No "magic"
    • Pro: Ability to use "find usages"
    • Pro: Easy to refactor with IDE tooling
    @AdamMc331
    #DCSF22 44

    View Slide

  65. Manual Approach
    • Pro: No "magic"
    • Pro: Ability to use "find usages"
    • Pro: Easy to refactor with IDE tooling
    • Con: A lot of code to write and maintain ourselves
    @AdamMc331
    #DCSF22 44

    View Slide

  66. Manual Approach
    • Pro: No "magic"
    • Pro: Ability to use "find usages"
    • Pro: Easy to refactor with IDE tooling
    • Con: A lot of code to write and maintain ourselves
    • Con: ReinvenBng the wheel
    @AdamMc331
    #DCSF22 44

    View Slide

  67. DI Library
    @AdamMc331
    #DCSF22 45

    View Slide

  68. DI Library
    • Pro: Reduced boilerplate code
    @AdamMc331
    #DCSF22 45

    View Slide

  69. DI Library
    • Pro: Reduced boilerplate code
    • Pro: Quick to get started
    @AdamMc331
    #DCSF22 45

    View Slide

  70. DI Library
    • Pro: Reduced boilerplate code
    • Pro: Quick to get started
    • Pro: More complex situa9ons like scoping are solved for us
    @AdamMc331
    #DCSF22 45

    View Slide

  71. DI Library
    • Pro: Reduced boilerplate code
    • Pro: Quick to get started
    • Pro: More complex situa9ons like scoping are solved for us
    • Con: Can feel like "magic"
    @AdamMc331
    #DCSF22 45

    View Slide

  72. DI Library
    • Pro: Reduced boilerplate code
    • Pro: Quick to get started
    • Pro: More complex situa9ons like scoping are solved for us
    • Con: Can feel like "magic"
    • Con: Harder to trace usages of dependencies
    @AdamMc331
    #DCSF22 45

    View Slide

  73. DI Library
    • Pro: Reduced boilerplate code
    • Pro: Quick to get started
    • Pro: More complex situa9ons like scoping are solved for us
    • Con: Can feel like "magic"
    • Con: Harder to trace usages of dependencies
    • Con: Requires knowledge of DI as a concept and the tool used
    @AdamMc331
    #DCSF22 45

    View Slide

  74. Library Considera.ons
    @AdamMc331
    #DCSF22 46

    View Slide

  75. Library Considera.ons
    • Some DI libraries can verify and manage dependencies at
    compile 8me
    @AdamMc331
    #DCSF22 46

    View Slide

  76. Library Considera.ons
    • Some DI libraries can verify and manage dependencies at
    compile 8me
    • This can lead to increased build 8mes as complexity grows
    @AdamMc331
    #DCSF22 46

    View Slide

  77. Library Considera.ons
    • Some DI libraries can verify and manage dependencies at
    compile 8me
    • This can lead to increased build 8mes as complexity grows
    • Some DI libraries use a service locator concept to fetch
    dependencies at run8me
    @AdamMc331
    #DCSF22 46

    View Slide

  78. Library Considera.ons
    • Some DI libraries can verify and manage dependencies at
    compile 8me
    • This can lead to increased build 8mes as complexity grows
    • Some DI libraries use a service locator concept to fetch
    dependencies at run8me
    • This keeps builds consistent, but can lead to unexpected
    errors at run8me if our dependency graph is broken
    @AdamMc331
    #DCSF22 46

    View Slide

  79. Trust Yourself
    Whether you choose a manual DI approach or one of the libraries
    out there will be up to you and your team. Chose what you are
    comfortable with, or what you think provides the most benefit to
    the size and complexity of your project.
    @AdamMc331
    #DCSF22 47

    View Slide

  80. Remember The Fundamentals
    @AdamMc331
    #DCSF22 48

    View Slide

  81. Remember The Fundamentals
    • The fundamental concepts of dependency injec5on provide
    support for be9er tes5ng, increasing our confidence in our
    products.
    @AdamMc331
    #DCSF22 48

    View Slide

  82. Remember The Fundamentals
    • The fundamental concepts of dependency injec5on provide
    support for be9er tes5ng, increasing our confidence in our
    products.
    • They also provide flexibility, and enable us to make broad changes
    to our product quickly.
    @AdamMc331
    #DCSF22 48

    View Slide

  83. Remember The Fundamentals
    • The fundamental concepts of dependency injec5on provide
    support for be9er tes5ng, increasing our confidence in our
    products.
    • They also provide flexibility, and enable us to make broad changes
    to our product quickly.
    • Having a dependency management system is a huge step in
    ensuring las5ng success of our applica5ons that can withstand the
    test of 5me.
    @AdamMc331
    #DCSF22 48

    View Slide

  84. Thanks!
    @AdamMc331
    #DCSF22 49

    View Slide