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

The Imposter's Guide To Dependency Injection - DCSF22

Adam McNeilly
June 02, 2022
290

The Imposter's Guide To Dependency Injection - DCSF22

Adam McNeilly

June 02, 2022
Tweet

Transcript

  1. Dependency Injec+on • Two big words • Half a dozen

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

    libraries • Countless Twi6er wars • Endless Confusion @AdamMc331 #DCSF22 2
  3. 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
  4. Try Tes'ng It class ProfileViewModelTest { @Test fun verifyEventTracked() {

    val viewModel = ProfileViewModel() viewModel.onProfileLoaded() // No Ability To Verify Event Tracked } } @AdamMc331 #DCSF22 7
  5. Constructor Injec-on class ProfileViewModel( private val analytics: FirebaseAnalytics = Firebase.analytics,

    ) { fun onProfileLoaded() { analytics.logEvent("viewed_profile") } } @AdamMc331 #DCSF22 10
  6. Updated Test class ProfileViewModelTest { @Test fun verifyEventTracked() { val

    mockAnalytics = FakeFirebaseAnalytics() val viewModel = ProfileViewModel(mockAnalytics) viewModel.onProfileLoaded() mockAnalytics.verifyEventLogged("viewed_profile") } } @AdamMc331 #DCSF22 12
  7. 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
  8. We Should Create Our Own Interface interface AnalyticsTracker { fun

    trackEvent(eventName: String) } @AdamMc331 #DCSF22 14
  9. With Our Own Implementa1on class FirebaseAnalyticsTracker : AnalyticsTracker { override

    fun trackEvent(eventName: String) { Firebase.analytics.logEvent(eventName) } } @AdamMc331 #DCSF22 15
  10. 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
  11. 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
  12. Instan&ate In Applica&on Class class MyApp : Application() { val

    appDependencies = AppDependencies() } @AdamMc331 #DCSF22 23
  13. 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
  14. They All Follow The Same Recipe • Create a collec+on

    of dependencies @AdamMc331 #DCSF22 27
  15. 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
  16. 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
  17. Dependency Container @Module @InstallIn(ActivityComponent::class) abstract class AnalyticsModule { @Binds abstract

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

    fun bindAnalyticsTracker( analyticsTracker: FirebaseAnalyticsTracker, ): AnalyticsTracker } @AdamMc331 #DCSF22 29
  19. Request Dependencies @AndroidEntryPoint class ProfileActivity : Activity() { @Inject lateinit

    var analyticsTracker: AnalyticsTracker } @AdamMc331 #DCSF22 31
  20. Applica'on Class class MyApp : Application() { override fun onCreate()

    { super.onCreate() startKoin { modules(analyticsModule) } } } @AdamMc331 #DCSF22 34
  21. Dependency Management Enables Several Benefits • Unit test support •

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

    Ability to change dependencies • Dependencies can be shared @AdamMc331 #DCSF22 37
  23. 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
  24. 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
  25. 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
  26. 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
  27. Manual Approach • Pro: No "magic" • Pro: Ability to

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

    use "find usages" • Pro: Easy to refactor with IDE tooling @AdamMc331 #DCSF22 44
  29. 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
  30. 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
  31. DI Library • Pro: Reduced boilerplate code • Pro: Quick

    to get started • Pro: More complex situa9ons like scoping are solved for us @AdamMc331 #DCSF22 45
  32. 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
  33. 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
  34. 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
  35. Library Considera.ons • Some DI libraries can verify and manage

    dependencies at compile 8me @AdamMc331 #DCSF22 46
  36. 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
  37. 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
  38. 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
  39. 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
  40. Remember The Fundamentals • The fundamental concepts of dependency injec5on

    provide support for be9er tes5ng, increasing our confidence in our products. @AdamMc331 #DCSF22 48
  41. 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
  42. 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