body: String) } class SMTPMailer(private val host: String): Mailer { override fun sendEmail(subject: String, to: String, body: String) { // call some SMTP server } } class NoOpMailer: Mailer { override fun sendEmail(subject: String, to: String, body: String) { // nothing! } }
a class Benefits: separation of concerns, configuration of a dependency Dependency inversion: abstraction to not depend on a specific implementation Benefits: switch implementation for different configurations / tests etc
• Runtime vs compile time validation • In essence still “pulling” dependencies, tighter coupling vs annotations (testing?) • Scoping is possible, but manual like Dagger
viewModel: MyViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel.sendEmail() } // already overridden by Hilt, you don’t have to implement this override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory { return super.getDefaultViewModelProviderFactory() } }
time safety • @Inject vs by inject() • Android constructs like viewModels() just work • Built-in Android aware scoping (if you need it) • Aggregation of modules (Koin annotations has similar goals)
{ @Provides @Singleton // there's probably no need to make Mailer singleton here fun provideMailer(): Mailer = if (BuildConfig.DEBUG) { NoOpMailer() } else { SMTPMailer("mail.mydomain.nl") } }
{ @Provides @Singleton // there's probably no need to make Mailer singleton here fun provideMailer(): Mailer = if (BuildConfig.DEBUG) { NoOpMailer() } else { SMTPMailer("mail.mydomain.nl") } }