11
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

依存性の注入を使うと何が嬉しいのか

11
Last updated at Posted at 2018-01-20

依存性の注入やDIコンテナってよく聞くのですが、いまいちメリットが良くわからない!
ということが多いので、AndroidのDIコンテナである、Daggerについてまとめました。

依存性の注入(Dependency injection)

依存性の注入(いそんせいのちゅうにゅう、英: Dependency injection)とは、
コンポーネント間の依存関係をプログラムのソースコードから排除し、外部の設定ファイルなどで注入できるようにするソフトウェアパターンである。
英語の頭文字からDIと略される。
https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E3%81%AE%E6%B3%A8%E5%85%A5

と説明されているのですが、実際どのようなメリットがあるのかよくわかりません。

依存性の注入が無いAndroidのコード例

retrofitを使って、APIに問い合わせするという例。

ArticleActivity.kt
class ArticleActivity : AppCompatActivity() {

    private val disposable = CompositeDisposable()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val articleId = intent.getIntExtra(ARTICLE_ID, 0)

        val retrofit = Retrofit.Builder()
            .addConverterFactory(MoshiConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build()
        val service = retrofit.create(ArticlesService::class.java)
        service.article(articleId)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io())
            .doOnSubscribe { disposable.add(it) }
            .subscribe({
                Log.v("ID", it.id.toString())
            }, {
                it.printStackTrace()
            })
    }
}
UserActivity.kt
class UserActivity : AppCompatActivity() {

    private val disposable = CompositeDisposable()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val articleId = intent.getIntExtra(USER_ID, 0)

        val retrofit = Retrofit.Builder()
            .addConverterFactory(MoshiConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build()
        val service = retrofit.create(UserService::class.java)
        service.article(articleId)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io())
            .doOnSubscribe { disposable.add(it) }
            .subscribe({
                Log.v("ID", it.id.toString())
            }, {
                it.printStackTrace()
            })
    }
}

このコードで問題点を上げるとするならば、

  • オブジェクト(インスタンス)の生成が多い
    • Retrofit => dispossable => service
    • オブジェクトの生成を列挙するという手続き的な書き方になってしまう
  • 複数のActivityで似たようなコードを使う
    • 変更に弱くなってしまい、保守が大変になる

DIを使わずに書いた場合

以下のようなラッパークラスを作って対応することができます。

ArticleActivity.kt
class ArticleActivity : AppCompatActivity() {

    private val disposable = CompositeDisposable()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val articleId = intent.getIntExtra(ARTICLE_ID, 0)

        ArticleClient().create().article(articleId)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io())
            .doOnSubscribe { disposable.add(it) }
            .subscribe({
                Log.v("ID", it.id.toString())
            }, {
                it.printStackTrace()
            })
    }
}
class ArticleClient {
    fun create() {
        val retrofit = Retrofit.Builder()
            .addConverterFactory(MoshiConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build()
        val service = retrofit.create(ArticleService::class.java)
    }
}

こう書くことによって、
retrofitとserviceの生成をArticleClientにまとめることができました。
そのため、オブジェクトの生成は減りました。
これによって保守性は少し上がったかもしれません。

ですが、結局オブジェクトの生成をなくすことができないです。
DI(依存性の注入)ではこの、オブジェクトの作成すらコードから排除しようという考え方です。

DIを使って書く

ここでDIとは一体何かを説明します。
日本語訳では依存性の注入と呼ばれていますが、実際はオブジェクトの注入が正しいです。
オブジェクトの注入とは何かというと、オブジェクトの作成をクラス内で行わずに、外部で定義して持ってくるということです。

導入方法については以下を参照してみて下さい
https://antonioleiva.com/dagger-android-kotlin/

導入すると以下のようなコードになると思います

ArticleActivity.kt
class ArticleActivity : AppCompatActivity() {

    val component by lazy { app.component.plus(HomeModule(this)) }

    @Inject lateinit var disposable: CompositeDisposable
    @Inject lateinit var service: ArticleService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        component.inject(this)

        val articleId = intent.getIntExtra(ARTICLE_ID, 0)

        service.article(articleId)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io())
            .doOnSubscribe { disposable.add(it) }
            .subscribe({
                Log.v("ID", it.id.toString())
            }, {
                it.printStackTrace()
            })
    }
}
AppModule.kt
@Module
class AppModule {
    @Provides
    fun provideCompositeDisposable(): CompositeDisposable = CompositeDisposable()

    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit {
        Retrofit.Builder()
            .addConverterFactory(MoshiConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build()
    }

    @Provides
    @Singleton
    fun provideArticleService(retrofit: Retrofit): ArticleService
        = retrofit.create(ArticleService::class.java)
}

オブジェクトの注入について

daggerでは@Providesで書かれたオブジェクトを@Injectがついた変数に入れるという機能があります。

上記の例のコードでは

@Inject lateinit var disposable: CompositeDisposable

@Provides
fun provideCompositeDisposable(): CompositeDisposable = CompositeDisposable()

が呼ばれます。
つまり、

val disposable: CompositeDisposable = CompositeDisposable()

と同じことがされます。

この機能によって、複数のActivityで定義されたオブジェクトの生成の共通化 などが行なえます。

オブジェクトの注入による省略

次に、以下のコードについて注目します

    @Provides
    @Singleton
    fun provideArticleService(retrofit: Retrofit): ArticleService
        = retrofit.create(ArticleService::class.java)

ここのretrofit: Retrofitという引数は

    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit {
        Retrofit.Builder()
            .addConverterFactory(MoshiConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build()
    }

から参照されます。

なので、変更前のコードでは、ArticleServiceのオブジェクトを生成するために、Retrofitのオブジェクトを生成していましたが、
Daggerを使うことによって、オブジェクトの生成のためのオブジェクトの生成の生成を省略できます

まとめ

Daggerを使うことによって

  • オブジェクトの注入(依存性の注入)ができる
  • オブジェクトの注入によってオブジェクトの生成をコードから省略できる
  • オブジェクトの生成に必要なオブジェクトを生成するというコードも省略できる

というメリットが得られます。

11
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?