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

今日から始める依存性の注入 / First Time Dependency Injection

今日から始める依存性の注入 / First Time Dependency Injection

DroidKaigi 2019
Room1, 2019/02/08 14:50~15:20

Keisuke Kobayashi

February 08, 2019
Tweet

More Decks by Keisuke Kobayashi

Other Decks in Programming

Transcript

  1. ࠓ೔͔Β࢝ΊΔґଘੑͷ஫ೖ
    First Time Dependency Injection
    Keisuke Kobayashi
    DroidKaigi 2019 / Room1, 2019/02/08 14:50~15:20

    View Slide

  2. ࣗݾ঺հ
    • Keisuke Kobayashi
    • Twitter: @kobakei122
    • GitHub: @kobakei
    • Merpay, Inc. / Engineering Manager
    • Studyplus, Inc. / ٕज़ސ໰ʢ෭ۀʣ

    View Slide

  3. ࠓ೔ͷςʔϚ
    • DIॳ৺ऀɺ·ͨ͸ʮงғؾͰDagger2Λ࢖͍ͬͯΔਓʯʹɺDI
    ͱ͸Ͳ͏͍͏΋ͷ͔ɺԿͷͨΊʹಋೖ͢Δͷ͔Λղઆ͢Δηο
    γϣϯͰ͢
    • ݸผͷDIίϯςφͷৄ͍͠࢖͍ํʹ͸࣌ؒͷؔ܎্ਂೖΓ͠·
    ͤΜ

    View Slide

  4. ΞδΣϯμ
    • Dependency Injection(DI)ͱ͸ʁ
    • AndroidΞϓϦ։ൃΛྫʹ঺հ
    • DIίϯςφͷجຊతͳ࢖͍ํ
    • Dagger2 & Koin
    • DIಋೖޙͷςετͷॻ͖ํ

    View Slide

  5. Dependency Injection (DI)ͱ͸ʁ
    • ίϯϙʔωϯτؒͷґଘؔ܎Λιʔείʔυ͔Βഉআ͠ɺ֎෦
    ͔ΒґଘίϯϙʔωϯτΛ஫ೖͤ͞ΔσβΠϯύλʔϯ
    • ґଘؔ܎Λഉআ͢Δ͜ͱͰɺίϯϙʔωϯτ͕ؒૄ݁߹ʹͳΔ

    View Slide

  6. DIͷϝϦοτ
    • ίϯϙʔωϯτ͕ؒૄ݁߹ʹͳΔ͜ͱͰɺҎԼͷϝϦοτ͕͋
    Δ
    • ΞϓϦέʔγϣϯΛ֦ு͠΍͘͢ͳΔ
    • ςετ͕ॻ͖΍͘͢ͳΔ
    • ͜Ε͸ޙͰৄ͘͠ղઆ

    View Slide

  7. AndroidΞϓϦ։ൃͰ
    Α͋͘ΔέʔεΛߟ͑Δ

    View Slide

  8. Α͋͘ΔMVPͷྫ
    HogeActivity
    HogePresenter
    UserRepository
    ApiClient UserDao

    View Slide

  9. Α͋͘ΔMVPͷྫ
    HogeActivity
    HogePresenter
    UserRepository
    ApiClient UserDao

    View Slide

  10. // DIΛಋೖ͍ͯ͠ͳ͍ίʔυ
    // ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε
    class UserRepository {
    private val apiClient: ApiClient = ApiClientImpl()
    private val userDao: UserDao = UserDaoImpl()
    fun find(id:Long): Single =
    return if (userDao.cached) {
    userDao.find(id)
    } else {
    apiClient.getUser(id)
    }
    }
    // UserRepositoryΛ࢖͏ଆ
    val repo = UserRepository()
    repo.find(123)...

    View Slide

  11. // DIΛಋೖ͍ͯ͠ͳ͍ίʔυ
    // ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε
    class UserRepository {
    private val apiClient: ApiClient = ApiClientImpl()
    private val userDao: UserDao = UserDaoImpl()
    fun find(id:Long): Single =
    return if (userDao.cached) {
    userDao.find(id)
    } else {
    apiClient.getUser(id)
    }
    }
    // UserRepositoryΛ࢖͏ଆ
    val repo = UserRepository()
    repo.find(123)...
    ґଘΦϒδΣΫτͱ

    ີ݁߹͍ͯ͠Δ

    View Slide

  12. // ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε
    class UserRepository(
    private val apiClient: ApiClient,
    private val userDao: UserDao
    ) {
    fun find(id:Long): Single =
    return if (userDao.cached) {
    userDao.find(id)
    } else {
    apiClient.getUser(id)
    }
    }
    // UserRepositoryΛ࢖͏ଆ
    val apiClient = ApiClientImpl()
    val userDao = UserDaoImpl()
    val repo = UserRepository(apiClient, userDao)
    repo.find(123)...

    View Slide

  13. // ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε
    class UserRepository(
    private val apiClient: ApiClient,
    private val userDao: UserDao
    ) {
    fun find(id:Long): Single =
    return if (userDao.cached) {
    userDao.find(id)
    } else {
    apiClient.getUser(id)
    }
    }
    // UserRepositoryΛ࢖͏ଆ
    val apiClient = ApiClientImpl()
    val userDao = UserDaoImpl()
    val repo = UserRepository(apiClient, userDao)
    repo.find(123)...
    ίϯετϥΫλͰ

    ґଘΦϒδΣΫτΛ
    ड͚औΔ

    View Slide

  14. // ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε
    class UserRepository(
    private val apiClient: ApiClient,
    private val userDao: UserDao
    ) {
    fun find(id:Long): Single =
    return if (userDao.cached) {
    userDao.find(id)
    } else {
    apiClient.getUser(id)
    }
    }
    // UserRepositoryΛ࢖͏ଆ
    val apiClient = ApiClientImpl()
    val userDao = UserDaoImpl()
    val repo = UserRepository(apiClient, userDao)
    repo.find(123)...
    ࢖͏ଆ͕ґଘΦϒδΣΫτ͔Β

    ૊Έཱ͍ͯͯΔ

    View Slide

  15. // ґଘΦϒδΣΫτΛղܾ͢ΔͨΊͷίϯςφΦϒδΣΫτ
    object Container {
    val apiClient: ApiClient = ApiClientImpl()
    val userDao: UserDao = UserDaoImpl()
    val userRepo: UserRepository =
    UserRepository(apiClient, userDao)
    }

    View Slide

  16. // ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε
    class UserRepository(
    private val apiClient: ApiClient,
    private val userDao: UserDao
    ) {
    fun find(id: Long): Single =
    return if (userDao.cached) {
    userDao.find(id)
    } else {
    apiClient.getUser(id)
    }
    }
    // UserRepositoryΛ࢖͏ଆ
    val repo = Container.userRepo
    repo.find(123)...
    UserRepositoryͷґଘؔ܎
    ͕ഉআ͞Εͨ

    View Slide

  17. Α͋͘ΔMVPͷྫ
    HogeActivity
    HogePresenter
    UserRepository
    ApiClient UserDao

    View Slide

  18. Activityͷ৔߹
    • ίϯετϥΫλͰͷ஫ೖ͕Ͱ͖ͳ͍
    • Fragment, Service, BroadcastReceiverͳͲ΋ಉ͡

    View Slide

  19. // DIΛಋೖ͍ͯ͠ͳ͍ίʔυ
    class HogeActivity : AppCompatActivity() {
    private val presenter = HogePresenter()
    override fun onCreate(savedInstanceState: Bundle?) {
    // লུ
    }
    fun onButtonClick(view: View) {
    presenter.onButtonClick()
    }
    }
    Activity͕
    HogePresenterʹ

    ີ݁߹͍ͯ͠Δ

    View Slide

  20. // DIΛಋೖ͍ͯ͠ͳ͍ίʔυ
    class HogeActivity : AppCompatActivity() {
    private lateinit var presenter: HogePresenter
    override fun onCreate(savedInstanceState: Bundle?) {
    // লུ
    presenter = HogePresenter(applicationContext)
    }
    fun onButtonClick(view: View) {
    presenter.onButtonClick()
    }
    }
    ͜Ε΋ີ݁߹͍ͯ͠Δ

    View Slide

  21. // ґଘΦϒδΣΫτΛղܾ͢ΔͨΊͷίϯςφΦϒδΣΫτ
    object Container {
    val apiClient: ApiClient = ApiClientImpl()
    val userDao: UserDao = UserDaoImpl()
    val userRepo: UserRepository =
    UserRepository(apiClient, userDao)
    private var hogePresenter: HogePresenter? = null
    fun resolveHogePresenter(context: Context): HogePresenter {
    if (hogePresenter == null) {
    hogePresenter = HogePresenter(context, userRepo)
    }
    return requireNotNull(hogePresenter)
    }
    }
    ContextΛҾ਺ʹऔΔϝιουΛ௥Ճ

    View Slide

  22. // DIΛಋೖޙͷίʔυ
    class HogeActivity : AppCompatActivity() {
    private val presenter: HogePresenter by lazy {
    Container.resolveHogePresenter(applicationContext)
    }
    override fun onCreate(savedInstanceState: Bundle?) {
    // লུ

    fun onButtonClick(view: View) {
    presenter.onButtonClick()
    }
    }
    by lazyΛ࢖͏͜ͱͰɺ
    Application ContextΛ౉ͤΔ

    View Slide

  23. ͜͜ͰؾʹͳΔ͜ͱ͕

    View Slide

  24. PresenterͷϥΠϑαΠΫϧ͜ΕͰେৎ෉ʁ
    • Presenter͸Activityͱಉ͡ϥΠϑαΠΫϧʹ͍ͨ͠
    • Activity͕ੜ੒͞ΕΔͱPresenter΋ੜ੒͞ΕɺActivity͕ഁغ
    ͞ΕΔͱಉ࣌ʹഁغ͞ΕΔ΂͖
    • ActivityͷΠϯελϯε͕2ͭ͋Δͱ͖͸ɺͦΕͧΕʹ
    Presenter͕͍ͯ΄͍͠

    View Slide

  25. είʔϓ
    • ґଘΦϒδΣΫτͷੜଘظؒΛද͢
    • SingletonɺActivityͱಉ͡ੜଘظؒɺͳͲ
    • είʔϓ͝ͱʹґଘΦϒδΣΫτ͕ੜ੒͞Εɺ࢖͍ճ͞ΕΔΑ
    ͏ͳ࢓૊Έ͕ඞཁ

    View Slide

  26. HogeActivity
    HogePresenter
    UserRepository
    ApiClient UserDao
    FugaActivity
    FugaPresenter

    View Slide

  27. HogeActivity
    HogePresenter
    UserRepository
    ApiClient UserDao
    FugaActivity
    FugaPresenter
    ը໘ڞ௨Ͱ࢖͍·Θ͍ͨ͠
    ֤ը໘͝ͱʹੜ੒͍ͨ͠

    View Slide

  28. HogeActivity
    HogePresenter
    UserRepository
    ApiClient UserDao
    FugaActivity
    FugaPresenter
    Singleton Singleton
    Singleton
    ActivityScope ActivityScope

    View Slide

  29. object Container {
    // লུ
    val userRepo = UserRepository(apiClient, logger)
    }
    class HogeActivityScopeContainer {
    fun resolveHogePresenter(ctx: Context): HogePresenter {…}
    }
    class FugaActivityScopeContainer {
    fun resolveFugaPresenter(ctx: Context): FugaPresenter {…}
    }

    View Slide

  30. object Container {
    // লུ
    val userRepo = UserRepository(apiClient, logger)
    }
    class HogeActivityScopeContainer {
    fun resolveHogePresenter(ctx: Context): HogePresenter {…}
    }
    class FugaActivityScopeContainer {
    fun resolveFugaPresenter(ctx: Context): FugaPresenter {…}
    }
    Singletonͳ
    ΫϥεͷΈ

    View Slide

  31. object Container {
    // লུ
    val userRepo = UserRepository(apiClient, logger)
    }
    class HogeActivityScopeContainer {
    fun resolveHogePresenter(ctx: Context): HogePresenter {…}
    }
    class FugaActivityScopeContainer {
    fun resolveFugaPresenter(ctx: Context): FugaPresenter {…}
    }
    HogeActivityͱಉ͡

    είʔϓͷΫϥεΛฦ͢

    View Slide

  32. // DIΛಋೖޙͷίʔυ
    class HogeActivity : AppCompatActivity() {
    private val container = HogeActivityScopeContainer()
    private val presenter: HogePresenter by lazy {
    container.resolveHogePresenter(applicationContext)
    }
    override fun onCreate(savedInstanceState: Bundle?) {
    // লུ

    fun onButtonClick(view: View) {
    presenter.onButtonClick()
    }
    }
    ͜ͷActivityͷ
    ίϯςφΛॳظԽ

    View Slide

  33. // DIΛಋೖޙͷίʔυ
    class HogeActivity : AppCompatActivity() {
    private val container = HogeActivityScopeContainer()
    private val presenter: HogePresenter by lazy {
    container.resolveHogePresenter(applicationContext)
    }
    override fun onCreate(savedInstanceState: Bundle?) {
    // লུ

    fun onButtonClick(view: View) {
    presenter.onButtonClick()
    }
    }
    containerΛ࢖ͬͯ஫ೖ

    View Slide

  34. ͜͜·Ͱͷ·ͱΊ
    • DIͱ͸ίϯϙʔωϯτؒͷґଘؔ܎Λ֎෦ʹ௥͍ग़͢ύλʔϯ
    • ґଘίϯϙʔωϯτΛ֎෦͔Βड͚औΔΑ͏ʹ͢Δ
    • είʔϓ͸ΦϒδΣΫτͷੜଘظؒΛද͠ɺείʔϓ͝ͱʹίϯςφ
    Λ࣋ͭ
    • ͜͜·Ͱͷ࣮૷͸͋͘·Ͱղઆ༻ͷࡶͳ΋ͷͳͷͰ஫ҙʂ

    ΑΓ࣮ફతͳ࣮૷ํ๏͸࣍Ҏ߱

    View Slide

  35. DIίϯςφ

    View Slide

  36. DIίϯςφ
    • DIΛ࣮ݱ͢ΔͨΊͷϥΠϒϥϦ
    • ઌఔࣗ෼Ͱ࣮૷ͨ͠ContainerʢΑΓ΋ͬͱ͍͍΋ͷʣΛࣗಈ
    Ͱ࡞ͬͯ͘ΕΔϥΠϒϥϦ

    View Slide

  37. AndroidͰ༗໊ͳDIίϯςφ
    • Dagger2
    • Koin
    • Kodein
    • Toothpick
    • Roboguice

    View Slide

  38. AndroidͰ༗໊ͳDIίϯςφ
    • Dagger2
    • Koin
    • Kodein
    • Toothpick
    • Roboguice

    View Slide

  39. Dagger2
    https://google.github.io/dagger/

    View Slide

  40. Dagger2
    • ݩʑSquare͕։ൃ => ݱࡏ͸Google͕fork
    • Annotation processorΛ࢖༻
    • ίϯύΠϧ࣌ʹґଘπϦʔʹ໰୊͕͋Δͱ෼͔Δ"
    • ίϯετϥΫλ͕࢖͑ΔΫϥεͷ৔߹ɺґଘؔ܎ͷղܾΛࣗಈੜ੒Ͱ͖Δ"
    • Ϗϧυ͸஗͘ͳΔ#
    • υΩϡϝϯτ͕೉͍͠###

    View Slide

  41. ஫ҙʂ
    • Dagger2͸͔ͳΓଟػೳͰ͕͢ɺ࣌ؒͷؔ܎্͋ͬ͞Γ͔͠঺հͰ͖·
    ͤΜ
    • Dagger2ʹ͸AndroidʹݶΒͳ͍௨ৗ൛ͱAndroidαϙʔτ൛
    ʢdagger.androidʣ͕͋Δ
    • ࠓ೔͸dagger.androidͰͷActivity΁ͷ஫ೖΛ঺հ
    • ʮ͓·͡ͳ͍ʯ͕େྔʹग़ͯ͘ΔͷͰ஫ҙ

    View Slide

  42. Dagger2ͷొ৔ਓ෺
    • Module
    • Component

    View Slide

  43. Module
    • ֤ґଘΫϥεΛͲ͏ΠϯελϯεԽ͢Δ͔Λఆٛ͢ΔΫϥε
    • Daggerͷ৔߹ίϯετϥΫλΛ࢖ͬͯΠϯελϯεԽ͢Δ

    Ϋϥε͸ࣗಈతʹੜ੒Ͱ͖Δɻ

    ίϯετϥΫλ͕࢖͑ͳ͍ΫϥεΛͲ͏΍ͬͯΠϯελϯεԽ
    ͢Δ͔͚ͩఆٛ͢Ε͹Α͍ɻ

    View Slide

  44. Α͋͘ΔMVPͷྫ
    HogeActivity
    HogePresenter
    UserRepository
    ApiClient UserDao

    View Slide

  45. class ApiClientImpl private constructor(): ApiClient {
    class Builder {
    fun build(): ApiClient {...}
    }
    override fun getUser(id: Long): Single {...}
    }
    class UserDaoImpl(): UserDao {
    override fun find(id: Long): Single {...}
    }
    class UserRepository(
    private val apiClient: ApiClient,
    private val userDao: UserDao
    ) {
    fun find(id: Long): Single {...}
    }

    View Slide

  46. class ApiClientImpl private constructor(): ApiClient {
    class Builder {
    fun build(): ApiClient {...}
    }
    override fun getUser(id: Long): Single {...}
    }
    class UserDaoImpl(): UserDao {
    override fun find(id: Long): Single {...}
    }
    class UserRepository(
    private val apiClient: ApiClient,
    private val userDao: UserDao
    ) {
    fun find(id: Long): Single {...}
    }
    ࣗಈੜ੒Ͱ͖Δ"

    =>Moduleʹॻ͘ඞཁͳ͠
    ࣗಈੜ੒Ͱ͖ͳ͍#

    => Moduleʹॻ͘ඞཁ͋Γ

    View Slide

  47. @Module
    class AppModule {
    @Singleton
    @Provides
    fun provideApiClient(): ApiClient {
    return ApiClientImpl.Builder().build()
    }
    }

    View Slide

  48. @Module
    class AppModule {
    @Singleton
    @Provides
    fun provideApiClient(): ApiClient {
    return ApiClientImpl.Builder().build()
    }
    }
    Dagger2ͷϞδϡʔϧΛҙຯ͢Δ

    View Slide

  49. @Module
    class AppModule {
    @Singleton
    @Provides
    fun provideApiClient(): ApiClient {
    return ApiClientImpl.Builder().build()
    }
    }

    View Slide

  50. @Module
    class AppModule {
    @Singleton
    @Provides
    fun provideApiClient(): ApiClient {
    return ApiClientImpl.Builder().build()
    }
    }
    ͜ͷϝιουΛґଘղܾʹ࢖͏

    View Slide

  51. @Module
    class AppModule {
    @Singleton
    @Provides
    fun provideApiClient(): ApiClient {
    return ApiClientImpl.Builder().build()
    }
    }
    ApiClientͷείʔϓ

    View Slide

  52. class ApiClientImpl private constructor(): ApiClient {
    class Builder {
    fun build(): ApiClient {...}
    }
    override fun getUser(id: Long): Single {...}
    }
    class UserDaoImpl(): UserDao {
    override fun find(id: Long): Single {...}
    }
    class UserRepository(
    private val apiClient: ApiClient,
    private val userDao: UserDao
    ) {
    fun find(id: Long): Single {...}
    }
    ࣗಈੜ੒Ͱ͖Δ"

    =>Moduleʹॻ͘ඞཁͳ͠
    ࣗಈੜ੒Ͱ͖ͳ͍#

    => Moduleʹॻ͘ඞཁ͋Γ

    View Slide

  53. @Singleton
    class UserRepository @Inject constructor(
    private val apiClient: ApiClient,
    private val userDao: UserDao
    ) {
    fun find(id: Long): Single {...}
    }
    ͜ͷίϯετϥΫλΛ࢖ͬͯΠϯελϯεԽ͞ΕΔ

    View Slide

  54. @Module
    abstract class ActivityModule {
    @ActivityScope
    @ContributesAndroidInjector
    abstract fun contributeHogeActivity(): HogeActivity
    }
    dagger.androidΛ࢖͏ͨΊͷಛघͳϞδϡʔϧ
    @ProvidesϝιουΛ࣋ͨͳ͍

    View Slide

  55. @Module
    abstract class ActivityModule {
    @ActivityScope
    @ContributesAndroidInjector
    abstract fun contributeHogeActivity(): HogeActivity
    }
    HogeActivityʹ஫ೖ͢ΔͨΊͷϝιου

    View Slide

  56. Component
    • ModuleΛ࢖ͬͯґଘ͍ͯ͠ΔΦϒδΣΫτΛੜ੒ɾ஫ೖ͢Δ

    Ϋϥε

    View Slide

  57. @Singleton
    @Component(modules = [
    AppModule::class,
    ActivityModule::class,
    AndroidInjectionModule::class
    ])
    interface AppComponent: AndroidInjector {
    @Component.Builder
    abstract class Builder: AndroidInjector.Builder()
    }

    View Slide

  58. @Singleton
    @Component(modules = [
    AppModule::class,
    ActivityModule::class,
    AndroidInjectionModule::class
    ])
    interface AppComponent: AndroidInjector {
    @Component.Builder
    abstract class Builder: AndroidInjector.Builder()
    }
    ͜ͷίϯϙʔωϯτ͕࢖͏
    Ϟδϡʔϧͷ഑ྻ

    View Slide

  59. @Singleton
    @Component(modules = [
    AppModule::class,
    ActivityModule::class,
    AndroidInjectionModule::class
    ])
    interface AppComponent: AndroidInjector {
    @Component.Builder
    abstract class Builder: AndroidInjector.Builder()
    }
    Android support༻ͷΠϯλϑΣʔεΛܧঝͤ͞Δ

    View Slide

  60. class App : Application(), HasActivityInjector {
    @Inject
    lateinit var injector: DispatchingAndroidInjector
    override fun onCreate() {
    super.onCreate()
    // Dagger2ͷॳظԽ
    DaggerAppComponent.builder()
    .create(this)
    .inject(this)
    }
    override fun activityInjector(): AndroidInjector
    = injector
    }

    View Slide

  61. class App : Application(), HasActivityInjector {
    @Inject
    lateinit var injector: DispatchingAndroidInjector
    override fun onCreate() {
    super.onCreate()
    // Dagger2ͷॳظԽ
    DaggerAppComponent.builder()
    .create(this)
    .inject(this)
    }
    override fun activityInjector(): AndroidInjector
    = injector
    }
    Android support༻ͷΠϯλϑΣʔεΛܧঝͤ͞Δ

    View Slide

  62. class App : Application(), HasActivityInjector {
    @Inject
    lateinit var injector: DispatchingAndroidInjector
    override fun onCreate() {
    super.onCreate()
    // Dagger2ͷॳظԽ
    DaggerAppComponent.builder()
    .create(this)
    .inject(this)
    }
    override fun activityInjector(): AndroidInjector
    = injector
    }
    AppʹinjectorΛ஫ೖ

    View Slide

  63. class HogeActivity : AppCompatActivity() {
    @Inject
    lateinit var presenter: HogePresenter
    override fun onCreate(savedInstanceState: Bundle?) {
    // লུ
    AndroidInjection.inject(this)
    }
    fun onButtonClick(view: View) {
    presenter.onButtonClick()
    }
    }

    View Slide

  64. class HogeActivity : AppCompatActivity() {
    @Inject
    lateinit var presenter: HogePresenter
    override fun onCreate(savedInstanceState: Bundle?) {
    // লུ
    AndroidInjection.inject(this)
    }
    fun onButtonClick(view: View) {
    presenter.onButtonClick()
    }
    }
    ґଘΦϒδΣΫτʹ͸
    @InjectΛ෇͚͓ͯ͘

    View Slide

  65. class HogeActivity : AppCompatActivity() {
    @Inject
    lateinit var presenter: HogePresenter
    override fun onCreate(savedInstanceState: Bundle?) {
    // লུ
    AndroidInjection.inject(this)
    }
    fun onButtonClick(view: View) {
    presenter.onButtonClick()
    }
    }
    ґଘΦϒδΣΫτΛ஫ೖ

    View Slide

  66. ͳΔ΄ͲɺΘ͔ΒΜ
    • େৎ෉
    • ଟ෼ΈΜͳ࠷ॳ͸෼͔ͬͯͳ͍
    • ࠓޙDaggerΛಋೖ͢Δͱ͖ʹɺ΋͏Ұ౓͜ͷࢿྉ΍ଞͷαϯϓ
    ϧΛݟ௚ͤ͹OK

    View Slide

  67. Koin
    https://insert-koin.io/

    View Slide

  68. Koin
    • KotlinͰॻ͔ΕͨDIίϯςφ
    • ҠৡϓϩύςΟΛ࢖༻ͯ͠ґଘੑΛղܾ
    • Annotation processorෆ࢖༻
    • Ϗϧυ଎౓ʹѱӨڹ͸ͳ͍"
    • ࣮ߦ͢Δ·ͰґଘπϦʔʹ໰୊͕ͳ͍͔෼͔Βͳ͍#
    • constructor injectionͰ͖ΔΫϥεͰ΋ɺϞδϡʔϧʹॻ͘ඞཁ͋Γ#
    • υΩϡϝϯτ͕Θ͔Γ΍͍͢"
    • AAC ViewModelʹରԠ"

    View Slide

  69. ࢖͍ํ
    1. moduleͷఆٛ
    • ͜͜͸Dagger2ͱಉ͡
    2. startKoinͰίϯςφ࡞੒
    3. ஫ೖઌͷΫϥεͰҠৡϓϩύςΟΛ࢖ͬͯ஫ೖ͢Δ

    View Slide

  70. class App : Application() {
    override fun onCreate() {
    super.onCreate()
    // KoinͷϞδϡʔϧͷઃఆ
    val appModule = module {
    single { LoggerImpl() }
    factory {
    ApiClientImpl.Builder().build()
    }
    factory {
    HogeRepository(get())
    }
    }
    // ґଘπϦʔͷ࡞੒
    startKoin(this, listOf(appModule))
    }
    }

    View Slide

  71. class App : Application() {
    override fun onCreate() {
    super.onCreate()
    // KoinͷϞδϡʔϧͷઃఆ
    val appModule = module {
    single { LoggerImpl() }
    factory {
    ApiClientImpl.Builder().build()
    }
    factory {
    HogeRepository(get())
    }
    }
    // ґଘπϦʔͷ࡞੒
    startKoin(this, listOf(appModule))
    }
    }
    Ϟδϡʔϧͷ࡞੒

    View Slide

  72. class App : Application() {
    override fun onCreate() {
    super.onCreate()
    // KoinͷϞδϡʔϧͷઃఆ
    val appModule = module {
    single { LoggerImpl() }
    factory {
    ApiClientImpl.Builder().build()
    }
    factory {
    HogeRepository(get())
    }
    }
    // ґଘπϦʔͷ࡞੒
    startKoin(this, listOf(appModule))
    }
    }
    Singleton

    View Slide

  73. class App : Application() {
    override fun onCreate() {
    super.onCreate()
    // KoinͷϞδϡʔϧͷઃఆ
    val appModule = module {
    single { LoggerImpl() }
    factory {
    ApiClientImpl.Builder().build()
    }
    factory {
    HogeRepository(get())
    }
    }
    // ґଘπϦʔͷ࡞੒
    startKoin(this, listOf(appModule))
    }
    }
    ΦϒδΣΫτΛ
    ຖճ࡞੒͢ΔΫϥε

    View Slide

  74. class App : Application() {
    override fun onCreate() {
    super.onCreate()
    // KoinͷϞδϡʔϧͷઃఆ
    val appModule = module {
    single { LoggerImpl() }
    factory {
    ApiClientImpl.Builder().build()
    }
    factory {
    HogeRepository(get())
    }
    }
    // ґଘπϦʔͷ࡞੒
    startKoin(this, listOf(appModule))
    }
    }
    getͰґଘղܾ

    View Slide

  75. class App : Application() {
    override fun onCreate() {
    super.onCreate()
    // KoinͷϞδϡʔϧͷઃఆ
    val appModule = module {
    single { LoggerImpl() }
    factory {
    ApiClientImpl.Builder().build()
    }
    factory {
    HogeRepository(get())
    }
    }
    // ґଘπϦʔͷ࡞੒
    startKoin(this, listOf(appModule))
    }
    } ίϯςφͷ࡞੒

    View Slide

  76. class HogeActivity : AppCompatActivity() {
    val logger: Logger by inject()
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ...
    logger.v(“Hello world!”)
    }
    }
    ҕৡϓϩύςΟΛ࢖ͬͯ஫ೖ

    View Slide

  77. AAC ViewModelʹରԠ
    • ViewModel͸Factoryܦ༝ͰΠϯελϯεΛੜ੒͢Δ
    • FactoryΛܧঝ͢Δ࢓૊Έ͕͋ΓɺDIͱ૊Έ߹ΘͤΔͷ͸

    τϦοΫ͕ඞཁ
    • Koin͸ରԠ͍ͯ͠Δʂ

    View Slide

  78. class HogeViewModel(
    private val userRepository: UserRepository
    ) : ViewModel() {…}

    View Slide

  79. class App : Application() {
    override fun onCreate() {
    super.onCreate()
    // KoinͷϞδϡʔϧͷઃఆ
    val appModule = module {
    // ...
    viewModel { HogeViewModel(get()) }
    }
    startKoin(this, listOf(appModule))
    }
    }
    ViewModelͷΠϯελϯεԽ

    View Slide

  80. class HogeActivity : AppCompatActivity() {
    val hogeViewModel: HogeViewModel by viewModel()
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ...
    }
    fun onButtonClick(view: View) {
    hogeViewModel.onButtonClick()
    }
    }
    ViewModel༻ͷ
    ҕৡϓϩύςΟΛ࢖͏

    View Slide

  81. ͜͜·Ͱͷ·ͱΊ
    • DIίϯςφ = DIΛ؆୯ʹ࣮૷͢ΔͨΊͷϥΠϒϥϦ
    • Dagger2…ଟػೳ͕ͩ೉͍͠ɻconstructor injectionͷ৔߹͸
    ϞδϡʔϧলུՄೳɻ
    • Koin…Θ͔Γ΍͍͢ɻAAC ViewModelʹରԠ͍ͯ͠Δɻ

    View Slide

  82. DIΛಋೖͯ͠Կ͕มΘͬͨʁ

    View Slide

  83. DIͷϝϦοτʢ࠶ܝʣ
    • ίϯϙʔωϯτ͕ؒૄ݁߹ʹͳΔ͜ͱͰɺҎԼͷϝϦοτ͕͋
    Δ
    • ΞϓϦέʔγϣϯΛ֦ு͠΍͘͢ͳΔ
    • ςετ͕ॻ͖΍͘͢ͳΔ
    • ͜Ε͸ޙͰৄ͘͠ղઆ

    View Slide

  84. ୯ମςετ

    View Slide

  85. // ςετର৅ͷΫϥεʢDIಋೖલʣ
    class UserRepository {
    private val apiClient: ApiClient = ApiClientImpl()
    fun find(id: Long): Single =
    apiClient.getUser(id)
    }
    ApiClientʹີ݁߹͍ͯ͠Δ

    View Slide

  86. // UserRepositoryͷςετίʔυ
    class UserRepositoryTest {
    @Test
    fun find_isSuccess() {
    val repo = UserRepository()
    repo.find(1)
    .test()
    .awaitCount(1)
    .assertValue(expected)
    }
    }

    View Slide

  87. // UserRepositoryͷςετίʔυ
    class UserRepositoryTest {
    @Test
    fun find_isSuccess() {
    val repo = UserRepository()
    repo.find(1)
    .test()
    .awaitCount(1)
    .assertValue(expected)
    }
    }
    ApiClientImplͷ

    ίʔυ΋࣮ߦ͍ͯ͠Δ

    View Slide

  88. HogeActivity
    HogeViewModel
    UserRepository
    ApiClient
    ͕͜͜ςετର৅ʹ
    ͳͬͯ͠·͍ͬͯΔ

    View Slide

  89. HogeActivity
    HogeViewModel
    UserRepository
    ApiClient
    ຊ౰ʹςετ͍ͨ͠ͷ͸
    ͚ͩ͜͜

    View Slide

  90. DIΛಋೖ͍ͯ͠Δ৔߹
    • ґଘίϯϙʔωϯτΛ஫ೖ͢Δͱ͖ʹɺςετʹ౎߹ͷ͍͍

    ΦϒδΣΫτΛ࢖͏͜ͱͰɺର৅ͷΫϥεͷΈݕূՄೳ
    • Mockk, MockitoͳͲͰϞοΫʹ͢Δ͜ͱ͕ଟ͍

    View Slide

  91. // ςετର৅ͷΫϥεʢDIಋೖޙʣ
    class UserRepository(
    private val apiClient: ApiClient
    ) {
    fun find(id: Long): Single =
    apiClient.getUser(id)
    }
    ApiClientΛ
    ֎෦͔Β஫ೖՄೳ

    View Slide

  92. // UserRepositoryͷςετίʔυ
    class UserRepositoryTest {
    @Test
    fun find_isSuccess() {
    val apiClient = mockk {
    every { getUser(any()) } returns Single.just(user)
    }
    val repo = UserRepository(apiClient)
    repo.find(1)
    .test()
    .awaitCount(1)
    .assertValue(user)
    }
    }

    View Slide

  93. // UserRepositoryͷςετίʔυ
    class UserRepositoryTest {
    @Test
    fun find_isSuccess() {
    val apiClient = mockk {
    every { getUser(any()) } returns Single.just(user)
    }
    val repo = UserRepository(apiClient)
    repo.find(1)
    .test()
    .awaitCount(1)
    .assertValue(user)
    }
    }
    ApiClientͷϞοΫΛ࡞੒

    View Slide

  94. // UserRepositoryͷςετίʔυ
    class UserRepositoryTest {
    @Test
    fun find_isSuccess() {
    val apiClient = mockk {
    every { getUser(any()) } returns Single.just(user)
    }
    val repo = UserRepository(apiClient)
    repo.find(1)
    .test()
    .awaitCount(1)
    .assertValue(user)
    }
    }
    ϞοΫΛ஫ೖͯ͠
    ςετ࣮ߦ

    View Slide

  95. ݁߹ςετ

    View Slide

  96. ݁߹ςετ
    • ???ʮͲ͏ͤ݁߹ͨ͠ঢ়ଶͰςετ͢Δ͔ΒDIͰίϯϙʔωϯ
    τؒΛૄ݁߹ʹͯ͠΋ҙຯͳ͍Μ͡Όͳ͍Μͷʁʯ
    • ͦΜͳ͜ͱ͸ͳ͍

    View Slide

  97. ྫ: EspressoͰUIςετΛॻ͘
    • APIΫϥΠΞϯτͷԠ౴͕ຖճҧ͏ͷͰਏ͍
    • Ϩεϙϯε͕࣌ؒʹΑͬͯҧ͏
    • Ԡ౴࣌ؒ
    • Α͘API͕յΕΔʢ։ൃ؀ڥͳͲʣ
    • DIΛಋೖ͍ͯ͠Δͱɺ؆୯ʹAPIΫϥΠΞϯτΛςετ༻ͷϞοΫʹࠩ͠ସ͑
    Δ͜ͱ͕Ͱ͖Δ

    View Slide

  98. DIίϯςφͷϞδϡʔϧΛςετ༻ʹ

    ࠩ͠ସ͑Δ
    1. ςετ༻ͷApplicationΛ࡞੒͠ɺ಺෦Ͱςετ༻Ϟδϡʔϧ
    Λ࢖ͬͯґଘπϦʔΛ࡞Δ
    2. Instrumented test࣮ߦ࣌ʹىಈ͢ΔApplicationΫϥεΛɺ

    1Ͱ࡞ͬͨΫϥεʹࠩ͠ସ͑Δ

    View Slide

  99. open class App : Application() {
    private val appModule = module {
    single { LoggerImpl() }
    factory { UserRepository(get()) }
    viewModel { MainViewModel(get(), get()) }
    }
    // APIΫϥΠΞϯτ༻Ϟδϡʔϧ
    protected open val apiModule = module {
    factory { ApiClientImpl() }
    }
    override fun onCreate() {
    super.onCreate()
    // ґଘπϦʔΛߏங
    startKoin(this, listOf(appModule, apiModule))
    }
    }

    View Slide

  100. open class App : Application() {
    private val appModule = module {
    single { LoggerImpl() }
    factory { UserRepository(get()) }
    viewModel { MainViewModel(get(), get()) }
    }
    // APIΫϥΠΞϯτ༻Ϟδϡʔϧ
    protected open val apiModule = module {
    factory { ApiClientImpl() }
    }
    override fun onCreate() {
    super.onCreate()
    // ґଘπϦʔΛߏங
    startKoin(this, listOf(appModule, apiModule))
    }
    }
    ςετ༻ͷAppΫϥεΛ

    ܧঝͯ͠࡞ΕΔΑ͏ʹ͢Δ

    View Slide

  101. open class App : Application() {
    private val appModule = module {
    single { LoggerImpl() }
    factory { UserRepository(get()) }
    viewModel { MainViewModel(get(), get()) }
    }
    // APIΫϥΠΞϯτ༻Ϟδϡʔϧ
    protected open val apiModule = module {
    factory { ApiClientImpl() }
    }
    override fun onCreate() {
    super.onCreate()
    // ґଘπϦʔΛߏங
    startKoin(this, listOf(appModule, apiModule))
    }
    }
    ͜͜΋ΦʔόʔϥΠυ
    Ͱ͖ΔΑ͏ʹ͢Δ

    View Slide

  102. class TestApp : App() {
    override val apiModule = module {
    factory {
    val user = User(123, “kobakei”)
    val apiClient = mockk {
    every { getUser(any()) } returns Single.just(user)
    }
    apiClient
    }
    }
    }

    View Slide

  103. class TestApp : App() {
    override val apiModule = module {
    factory {
    val user = User(123, “kobakei”)
    val apiClient = mockk {
    every { getUser(any()) } returns Single.just(user)
    }
    apiClient
    }
    }
    } ϞοΫͷAPIΫϥΠΞϯτΛฦ͢
    ϞδϡʔϧͰΦʔόʔϥΠυ

    View Slide

  104. // ΞϓϦέʔγϣϯΛࠩ͠ସ͑ΔͨΊͷϥϯφʔ
    class MyTestRunner : AndroidJUnitRunner() {
    override fun newApplication(cl: ClassLoader?, name: String?, c: Context?) =
    super.newApplication(cl, TestApp::class.java.name, c)
    }
    TestAppΫϥεΛىಈ͢Δ

    View Slide

  105. // app/build.gradle
    android {
    defaultConfig {
    testInstrumentationRunner “your.app.MyTestRunner”
    }
    }
    Instrumented test࣌ͷϥϯφʔΛࢦఆ

    View Slide

  106. ຊ೔ͷ·ͱΊ

    View Slide

  107. ·ͱΊ
    • DI = ίϯϙʔωϯτؒͷґଘؔ܎Λ֎෦ʹ௥͍ग़͢σβΠϯύ
    λʔϯ
    • DIΛಋೖ͢Δ͜ͱͰɺίϯϙʔωϯτ͕ૄ݁߹ʹͳΓɺίϯ
    ϙʔωϯτΛࠩ͠ସ͑ͨΓɺςετ͕ॻ͖΍͘͢ͳΔ
    • Dagger2΍KoinͳͲDIίϯςφΛར༻͠Α͏

    View Slide

  108. Thanks!!!

    View Slide