Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Upcoming SlideShare
What to Upload to SlideShare
What to Upload to SlideShare
Loading in …3
×
1 of 151

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

Dagger 2, 2 years later

  1. 1. http://bit.ly/timewarning http://bit.ly/speedwarning
  2. 2. –Me, ~2 years ago “My future team will ❤ me for all this Dagger work I’m doing!”
  3. 3. –The team, ~2 months ago “We hate Dagger and we hate you.”
  4. 4. Don’t use Dagger 2
  5. 5. Don’t use Dagger 2
  6. 6. 👍
  7. 7. Dagger 2, 2 Years Later
  8. 8. Dagger 2, 2 Years Later
  9. 9. ––Erik Dietrich, “How Developers Stop Learning: Rise of the Expert Beginner” “…in software, feedback cycles tend to be on the order of months, if not years…It’s during the full lifetime of a project that a developer gains experience writing code…modifying it, testing it, and living with previous design and architecture decisions during maintenance phases. With everything I’ve just described, a developer is lucky to have a first try of less than six months…”
  10. 10. ––Erik Dietrich, “How Developers Stop Learning: Rise of the Expert Beginner” “…in software, feedback cycles tend to be on the order of months, if not years…It’s during the full lifetime of a project that a developer gains experience writing code…modifying it, testing it, and living with previous design and architecture decisions during maintenance phases. With everything I’ve just described, a developer is lucky to have a first try of less than six months…”
  11. 11. –Jake Wharton, “The Future of Dependency Injection with Dagger 2” “The entire public API of Dagger 2 is right here [on this single slide]…so the only hard part about using Dagger 2 is really understanding the fundamentals of dependency injection and the concept of each one of these individual things: how you provide dependencies, how you request dependencies, and then the component interface of how you link the two together.”
  12. 12. –Donn Felker “When Dagger 1 came out it was just simpler and then Dagger 2 [came out and] I felt like it got really complicated”
  13. 13. –Donn Felker “When Dagger 1 came out it was just simpler and then Dagger 2 [came out and] I felt like it got really complicated” “I think there’s a shared understanding that there’s something wrong [with Dagger 2]. Something’s not working.” –Kaushik Goupal “Episode 133,” Fragmented
  14. 14. “Something’s not working”
  15. 15. Why?
  16. 16. –John Dewey “A problem well put is half solved” (philosopher 🙌)
  17. 17. class ScheduleFragment : MainNavigationFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { !// Set up search menu item binding.includeScheduleAppbar.toolbar.run { setOnMenuItemClickListener { item -> if (item.itemId !== R.id.search) { analyticsHelper.logUiEvent("Navigate to Search", AnalyticsActions.CLICK) } else { //… } } } analyticsHelper.sendScreenView("Schedule", requireActivity()) } }
  18. 18. class ScheduleFragment : MainNavigationFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { !// Set up search menu item binding.includeScheduleAppbar.toolbar.run { setOnMenuItemClickListener { item -> if (item.itemId !== R.id.search) { analyticsHelper.logUiEvent("Navigate to Search", AnalyticsActions.CLICK) } else { //… } } } analyticsHelper.sendScreenView("Schedule", requireActivity()) } }
  19. 19. class ScheduleFragment : MainNavigationFragment() { @Inject lateinit var analyticsHelper: AnalyticsHelper override fun onViewCreated(view: View, savedInstanceState: Bundle?) { !// Set up search menu item binding.includeScheduleAppbar.toolbar.run { setOnMenuItemClickListener { item -> if (item.itemId !== R.id.search) { analyticsHelper.logUiEvent("Navigate to Search", AnalyticsActions.CLICK) } else { //… } } } analyticsHelper.sendScreenView("Schedule", requireActivity()) } }
  20. 20. class AnalyticsHelper constructor( context: Context, signInViewModelDelegate: SignInViewModelDelegate, preferenceStorage: PreferenceStorage )
  21. 21. class AnalyticsHelper @Inject constructor( context: Context, signInViewModelDelegate: SignInViewModelDelegate, preferenceStorage: PreferenceStorage )
  22. 22. class ScheduleFragment : MainNavigationFragment() { @Inject lateinit var analyticsHelper: AnalyticsHelper override fun onViewCreated(view: View, savedInstanceState: Bundle?) { !// Set up search menu item binding.includeScheduleAppbar.toolbar.run { setOnMenuItemClickListener { item -> if (item.itemId !== R.id.search) { analyticsHelper.logUiEvent("Navigate to Search", AnalyticsActions.CLICK) } else { //… } } } analyticsHelper.sendScreenView("Schedule", requireActivity()) } }
  23. 23. dagger-android or boilerplate?
  24. 24. AnalyticsHelper
  25. 25. com.other.library.AnalyticsHelper
  26. 26. @Provides fun providesAnalyticsHelper( context: Context, signInDelegate: SignInViewModelDelegate, preferenceStorage: PreferenceStorage ): AnalyticsHelper = …
  27. 27. What module does this go in?
  28. 28. @Provides fun providesAnalyticsHelper( context: Context, signInDelegate: SignInViewModelDelegate, preferenceStorage: PreferenceStorage ): AnalyticsHelper = …
  29. 29. Binds vs. Provides? In Kotlin?
  30. 30. @Binds abstract fun providesAnalyticsHelper( context: Context, signInDelegate: SignInViewModelDelegate, preferenceStorage: PreferenceStorage ): AnalyticsHelper
  31. 31. Scoped? Which one?
  32. 32. @Binds abstract fun providesAnalyticsHelper( context: Context, signInDelegate: SignInViewModelDelegate, preferenceStorage: PreferenceStorage ): AnalyticsHelper
  33. 33. @Singleton @Binds abstract fun providesAnalyticsHelper( context: Context, signInDelegate: SignInViewModelDelegate, preferenceStorage: PreferenceStorage ): AnalyticsHelper
  34. 34. Do I need a qualifier?
  35. 35. @Singleton @Binds abstract fun providesAnalyticsHelper( context: Context, signInDelegate: SignInViewModelDelegate, preferenceStorage: PreferenceStorage ): AnalyticsHelper
  36. 36. @Singleton @Binds @Named(“😢”) abstract fun providesAnalyticsHelper( context: Context, signInDelegate: SignInViewModelDelegate, preferenceStorage: PreferenceStorage ): AnalyticsHelper = …
  37. 37. Late-bound dependencies?
  38. 38. interface AppComponent : AndroidInjector<MainApplication> { @Component.Factory interface Factory { fun create(@BindsInstance application: MainApplication): AppComponent } }
  39. 39. Builder? Factory? AssistedInject?
  40. 40. 🔁???
  41. 41. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Are the dependencies of this dependency in the graph? If not, 🔁
  42. 42. It gets worse
  43. 43. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Are the dependencies of this dependency in the graph? If not, 🔁
  44. 44. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Are the dependencies of this dependency in the graph? If not, 🔁
  45. 45. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Are the dependencies of this dependency in the graph? If not, 🔁
  46. 46. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • ☝, but don’t forget testing • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Are the dependencies of this dependency in the graph? If not, 🔁
  47. 47. –Your teammate, helpfully “Don’t try to instantiate that until X”
  48. 48. public static synchronized Database getInstance(Context context, AccountProvider accountProvider) { sInstance = new Database(); sInstance.initialize(context, accountProvider) return sInstance; }
  49. 49. public static synchronized Database getInstance(Context context, AccountProvider accountProvider) { sInstance = new Database(); sInstance.initialize(context, accountProvider) return sInstance; }
  50. 50. • dagger-android or boilerplate? • ☝, but don’t forget testing • If I can’t just add @Inject to the constructor, what module does this go in? • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Are the dependencies of this dependency in the graph? If not, 🔁
  51. 51. • dagger-android or boilerplate? • ☝, but don’t forget testing • If I can’t just add @Inject to the constructor, what module does this go in? • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Do I need to inject Lazy<T> or Provides<T>? • Are the dependencies of this dependency in the graph? If not, 🔁
  52. 52. Why?
  53. 53. Why?
  54. 54. Cost App Size Service Locator Dagger Manual DI
  55. 55. Cost App Size Manual DI
  56. 56. Is DI worth the cost here and now?
  57. 57. Is DI worth the cost here and now?
  58. 58. –Martin Fowler, “Is TDD Dead?” Pt 5 ~35:00 “I’m actually always suspicious when I’m writing up a pattern or a technique if I can’t talk about reasons when you shouldn’t use and trade-offs against it. If I can’t find arguments against it, then I’m worrying that I’m not really analyzing things properly.”
  59. 59. Is DI worth the cost here and now?
  60. 60. UseCase
  61. 61. UseCaseRepo
  62. 62. UseCaseRepoDataSource
  63. 63. UseCaseRepoDataSource…
  64. 64. val viewModel = ScheduleViewModel( LoadFilteredUserSessionsUseCase( DefaultSessionAndUserEventRepository( FirestoreUserEventDataSource(FirebaseFirestore.getInstance()), DefaultSessionRepository( ConferenceDataRepository( NetworkConferenceDataSource( context, NetworkUtils(context) ), BootstrapConferenceDataSource, AppDatabase_Impl() ) ) ) ) )
  65. 65. val userEventRepository = DefaultSessionAndUserEventRepository( FirestoreUserEventDataSource(FirebaseFirestore.getInstance()), DefaultSessionRepository( ConferenceDataRepository( NetworkConferenceDataSource( context, NetworkUtils(context) ), BootstrapConferenceDataSource, AppDatabase_Impl() ) ) ) val preferenceStorage = SharedPreferenceStorage(context) val viewModel = ScheduleViewModel( LoadFilteredUserSessionsUseCase( userEventRepository ), LoadEventFiltersUseCase( TagRepository( ConferenceDataRepository( NetworkConferenceDataSource( context, NetworkUtils(context) ), BootstrapConferenceDataSource, AppDatabase_Impl() ) ) ), SignInViewModelDelegate(), StarEventAndNotifyUseCase( userEventRepository, StarReserveNotificationAlarmUpdater( SessionAlarmManager(context) ) ), ScheduleUiHintsShownUseCase( preferenceStorage ), FcmTopicSubscriber(), SnackbarMessageManager(preferenceStorage), GetTimeZoneUseCase(preferenceStorage), RefreshConferenceDataUseCase( ConferenceDataRepository( ) ) )
  66. 66. …🙄
  67. 67. val viewModel = ScheduleViewModel()
  68. 68. Is DI worth the cost here and now?
  69. 69. –50% of the audience “Yes! Because SOLID.”
  70. 70. –50% of the audience “Yes! Because SOLID.”
  71. 71. –Uncle Bob, Principles and Patterns “…Clearly such a restriction is draconian…if you have tried and true modules that are concrete, but not volatile, depending upon them is not so bad.”
  72. 72. public MovieLister() { finder = new ColonDelimitedMovieFinder("movies1.txt"); }
  73. 73. –Martin Fowler, “IoC Containers and the Dependency Injection pattern” “Now if I'm using this class for just myself, this is all fine and dandy.”
  74. 74. –Testing Fans “But what about testing?!”
  75. 75. @Test fun reservationReceived() { !// !!... val loadSessionsUseCase = createTestLoadUserSessionsByDayUseCase(source) val viewModel = createScheduleViewModel( loadFilteredSessionsUseCase = loadSessionsUseCase, !// !!... ) }
  76. 76. class ScheduleViewModel constructor( private val sessionsUseCase: LFUserSessionsUseCase = LFUserSessionsUseCase() !//… ) : ViewModel()
  77. 77. class ScheduleViewModel constructor( private val sessionsUseCase: LFUserSessionsUseCase = LFUserSessionsUseCase() !//… ) : ViewModel()
  78. 78. val viewModel = ScheduleViewModel()
  79. 79. Cost App Size Manual DI
  80. 80. Cost App Size Dagger
  81. 81. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • ☝, but don’t forget testing • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Do I need to inject Lazy<T> or Provides<T>? • Are the dependencies of this dependency in the graph? If not, 🔁
  82. 82. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • ☝, but don’t forget testing • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Do I need to inject Lazy<T> or Provides<T>? • Are the dependencies of this dependency in the graph? If not, 🔁
  83. 83. –My team, naively “We’ll just add Dagger as we go”
  84. 84. let go
  85. 85. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • ☝, but don’t forget testing • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Do I need to inject Lazy<T> or Provides<T>? • Are the dependencies of this dependency in the graph? If not, 🔁
  86. 86. –Joe Armstrong “If you’re ever in a big project and you’re trying to find out how it works and you talk to people and they say, ‘I don’t understand how this works but someone else understands,’ then you can be sure that you’re in a project that’s going to go pear- shaped”
  87. 87. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • ☝, but don’t forget testing • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Do I need to inject Lazy<T> or Provides<T>? • Are the dependencies of this dependency in the graph? If not, 🔁
  88. 88. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • ☝, but don’t forget testing • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Do I need to inject Lazy<T> or Provides<T>? • Are the dependencies of this dependency in the graph? If not, 🔁
  89. 89. Dagger components as service locators
  90. 90. –Martin Fowler “I've often heard the complaint that these kinds of service locators are a bad thing because they aren't testable because you can't substitute implementations for them. Certainly you can design them badly to get into this kind of trouble, but you don't have to.”
  91. 91. @Singleton @Component(modules = BluetoothComponent.Module.class) public interface BluetoothComponent { BluetoothAdvertiser ble(); }
  92. 92. public class MainActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ((Application) getApplication()) .getBluetoothComponent() .ble() .startAdvertising(); }
  93. 93. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • ☝, but don’t forget testing • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Do I need to inject Lazy<T> or Provides<T>? • Are the dependencies of this dependency in the graph? If not, 🔁
  94. 94. Consider avoiding needless Interface/Impl Pairs
  95. 95. @Module abstract class ViewModelModule { @Binds internal abstract fun bindViewModelFactory(factory: IOSchedViewModelFactory): ViewModelProvider.Factory }
  96. 96. –Martin Fowler, “InterfaceImplementationPair” “This isn’t…a technique that I've ever much liked. Using interfaces when you aren't going to have multiple implementations is extra effort to keep everything in sync (although good IDEs help). Furthermore it hides the cases where you actually do provide multiple implementations.”
  97. 97. Consider avoiding needless Interface/Impl Pairs
  98. 98. Consider avoiding needless Interface/Impl Pairs (or just depend on the implementation)
  99. 99. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • ☝, but don’t forget testing • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Do I need to inject Lazy<T> or Provides<T>? • Are the dependencies of this dependency in the graph? If not, 🔁
  100. 100. @Singleton @Component(modules = [FakeNetworkModule!::class]) interface TestApplicationComponent : ApplicationComponent { }
  101. 101. @Module class FakeNetworkModule { @Provides fun provideLoginRetrofitService(): LoginRetrofitService { return FakeLoginService() } } @Singleton @Component(modules = [FakeNetworkModule!::class]) interface TestApplicationComponent : ApplicationComponent { }
  102. 102. Consider late-bound dependencies instead of “published” modules
  103. 103. @Component interface ApplicationComponent { @Component.Factory interface Factory { fun make(@BindsInstance loginService: LoginService): ApplicationComponent } }
  104. 104. @Component interface ApplicationComponent { @Component.Factory interface Factory { fun make(@BindsInstance loginService: LoginService): ApplicationComponent } } !// Application code ApplicationComponent.factory().make(loginService)
  105. 105. @Component interface ApplicationComponent { @Component.Factory interface Factory { fun make(@BindsInstance loginService: LoginService): ApplicationComponent } } !// Application code ApplicationComponent.factory().make(loginService) !// Test code ApplicationComponent.factory().make(fakeLoginService)
  106. 106. Consider component dependencies instead of “published” modules
  107. 107. @Component(dependencies = [NetworkComponent!::class]) interface ApplicationComponent {} @Component interface NetworkComponent { fun loginService(): LoginService }
  108. 108. !// Application Code val networkComponent = DaggerNetworkComponent.factory() .create(this) applicationComponent = DaggerApplicationComponent.factory() .create( this, networkComponent )
  109. 109. !// Test Code postListRepository = mock(PostListRepository!::class.java) testApp.initializeApplicationComponent(object : NetworkComponent { override fun repo(): PostListRepository = postListRepository })
  110. 110. !// Test Code postListRepository = mock(PostListRepository!::class.java) testApp.initializeApplicationComponent(object : NetworkComponent { override fun repo(): PostListRepository = postListRepository })
  111. 111. Cost App Size Dagger
  112. 112. Dagger 2, 2 Years Later
  113. 113. @philosohacker (looking for a new challenge)
  114. 114. “Bonus Features”
  115. 115. The OG Reason for DI
  116. 116. “A common issue to deal with is how to wire together different elements: how do you fit together this web controller architecture with that database interface backing when they were built by different teams with little knowledge of each other…” –Martin Fowler, “IoC Containers and the Dependency Injection pattern”
  117. 117. “A common issue to deal with is how to wire together different elements: how do you fit together this web controller architecture with that database interface backing when they were built by different teams with little knowledge of each other…” –Martin Fowler, “IoC Containers and the Dependency Injection pattern”
  118. 118. –Kent Beck, “Is TDD Dead?” Pt. 1 ~21:05 “My personal practice is I mock almost nothing. If I can’t figure out how to test efficiently with the real stuff, I find another way of creating a feedback loop…”
  119. 119. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • ☝, but don’t forget testing • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Do I need to inject Lazy<T> or Provides<T>? • Are the dependencies of this dependency in the graph? If not, 🔁
  120. 120. • dagger-android or boilerplate? • If I can’t just add @Inject to the constructor, what module does this go in? • ☝, but don’t forget testing • What about @Binds vs. @Provides in Kotlin? • Does this need to be scoped? If so, which one? • Do I need a qualifier? • Late-bound dependencies? • Builder, Factory, or Assisted inject? • Do I need to inject Lazy<T> or Provides<T>? • Are the dependencies of this dependency in the graph? If not, 🔁
  121. 121. Have a strong opinion on module location
  122. 122. –You, frustrated “Why does this depend on a Context?”
  123. 123. Consider avoiding needless Interface/Impl Pairs
  124. 124. Don’t add a new provides method for every “new”

×