LoginSignup
25
16

More than 1 year has passed since last update.

Daggerってなに!

Last updated at Posted at 2023-01-12

この記事は

Daggerって最近知ったんだけど、なんかよくわからない!なんかむずかしそう!と思っている方向けです。
この記事を読むと、「Daggerきいたことある」から「Daggerちょっと知ってる」になれます。
実は筆者である私もDaggerが非常に苦手なのですが、最近少し勉強したのでここにまとめていきたいと思います!

⚠️ この記事のサンプルコードは簡易化しているため実際に動くわけではありません

Daggerとは

DaggerはDIライブラリです!

DIってなに??

DIとはDependency Injectionの略で、依存性の注入を意味します!
つまりDaggerは依存性の注入(DI)を行うためのライブラリです。

とはいえ、別にDaggerがないとDIができないというわけではありません。

例えば、ニュースアプリを題材に考えてみましょう。
次のようなソースコードをみなさんもどこかで見た経験があるかと思います。

class NewsListViewModel(
    private val newsRepository: NewsRepository,
): ViewModel(){
}

class NewsRepository(
    private val newsDataSource: NewsDataSource,
){
}

class NewsDataSource(
    private val newsApi: NewsApi,
){
}

これらのクラスはコンストラクタで依存性を注入しています。
これも一種のDIで、コンストラクタインジェクションといいます。

あるクラスを他のクラスで直接インスタンス生成することを避けていて

  • テストがしやすい
  • interfaceに依存させている場合、実装を差し替えるのが容易

などの利点があります。

じゃあ別にDagger使わなくてもよくね〜〜〜?

たしかに、DaggerがなくてもDIできるなら、Daggerを使う必要はないかのように思えます。

Daggerを使わない場合どうなるのでしょうか。

class NewsListActivity() {
    lateinit var viewModel: NewsListViewModel

    override fun onCreate() {
        val newsApi = Retrofit.Builder().baseUrl()
        val newsDataSource = NewsDataSource(newsApi)
        val newsRepository = NewsRepository(newsDataSource)
        
        // AACのViewModelはこのように直接Activityなどでインスタンス生成するのはNG
        // サンプルなので直接インスタンス化してるだけです、本当はダメ!
        viewModel = NewsListViewModel(newsRepository)
    }

}

ActivityでviewModelのデータを使いたいだけなのに、NewsApiをインスタンス化し、そのインスタンスをNewsRepositoryに渡してNewsRepositoryをインスタンス化し、そのインスタンスを利用してやっとViewModelのインスタンスが作れました。ふぅ。

これだけでも大変そうですが、例えば

  • ViewModelが複数のRepositoryに依存しだしたら?
  • Repositoryが複数のDataSourceに依存しだしたら?
  • Repositoryのインスタンスをアプリ内で一つだけ(シングルトン)にしたくなったら??

これでは対応がつらい!

Daggerパイセンたすけてください!

Dagger Hiltを使うとあっというまにコードがすっきり!

@Module 
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Singleton
    @Provides
    fun provideNewsApi(): NewsApi {
        val retrofit = Retrofit.Builder.baseUrl()
        return retrofit.create(NewsApi::class.java)
    }
}

Daggerでインスタンスを生成してほしいとき

  1. @Moduleをつけたクラス内で@Providesアノテーションによりインスタンスを配布する方法
  2. インスタンスを生成してほしいクラスのコンストラクタに@Injectアノテーションをつける方法

があります。

サードパーティーライブラリを使う場合など、直接クラスのコンストラクタを変更できない場合は1のパターンでDIします。
前述のサンプルコードはRetrofitというサードパーティライブラリを使用しているため、@Provideでインスタンスを返しています。

@HiltViewModel
class NewsListViewModel @Inject constractor(
    private val newsRepository: NewsRepository,
): ViewModel(){
}

@Singleton
class NewsRepository @Inject constractor(
    private val newsDataSource: NewsDataSource,
){
}

@Singleton
class NewsDataSource @Inject constractor(
    private val newsApi: NewsApi,
){
}

一方、こちらは自作のクラスなので直接コンストラクタを変更できます。
このような場合は@Injectを使う2のケースでDIします。

また、シングルトンにしたい場合は@Singletonをつけます。
ViewModelには@HiltViewModelをつけてあげるのも必須です。

@AndroidEntryPoint
class NewsListActivity() {
    private val viewModel: NewsListViewModel by viewModels()
}

Dagger Hiltを使う場合、Dagger Hiltで依存性が注入されるActivity/Fragment/Serviceなどに@AndroidEntryPointのアノテーションを付ける事が必須です。

Daggerは、各クラスの依存関係のグラフを内部的に作って、依存関係ツリーの最下層から順番にインスタンスを作成してくれます。
今回の場合だと、インスタンスを生成してくれる順番は次の通りです。

NewsApi -> NewsDataSource -> NewsRepository -> NewsListViewModel

アノテーションを正しく付与することで、Daggerパイセンが適切な順番で適切なインスタンスを適切なクラスに勝手に配布してくれます!(優秀!)

逆に人間側がアノテーションを正しく付与しないと、依存関係が解決できず、 Daggerのエラーが起きてしまいます。

[小話] ちょっと待って!Hiltってなに?!

いやDaggerの解説をしてくれる記事だと思って読んでいたけど途中からHilt(Dagger Hilt)ってのが出てきちゃってんだが。
大丈夫そ??...と、思っている方がいるかもしれませんね。(とてもよくわかりますヨ)

実は、Daggerナニモワカラナイな時、私の身にもよくそんなことが起きていました。。。

〜先輩に質問中〜

私「Daggerって〜〜〜〜〜なんですか?」

先輩「そうですね!ま、DaggerっていうかHiltはそう、って感じですけど!」

私「(は???DaggerとHiltって違うんですか???)」

私「(てかHiltってなにです???)」

〜〜〜回想終了〜〜〜

Daggerとは、javaの世界でDIするためのライブラリです。
Dagger Android Supportというのもあって、これはライフサイクルなどAndroid独自のルールが考慮されており、Daggerと組み合わせて使うものです。しっかしこれはひっじょーーーーーに扱いがむずかしいです。

そこでDagger Hiltくんが登場します。

Dagger Hiltは、Dagger Android Supportをより簡単に使えるようにしたもので、Daggerライブラリを使って構築されたAndroid用の便利なDIライブラリなのです(参考)。

なのでAndroid開発でDIしたい場合、Dagger Hiltを使うことがおすすめされますし、Dagger Android Supportを使っている場合はDagger Hiltに移行するのが良いでしょう。

(この記事でも、Dagger Hiltを前提に話を進めております。:bow:

[蛇足] クラスじゃなくてインタフェースを配りたいとき

@Moduleをつけたクラス内で@Providesする以外にも@Bindsを使う方法もあります。(よく知らない)
参考:@Binds を使用してインターフェース インスタンスを注入する

まとめ

  • DaggerはDIライブラリ
  • DIとは依存性の注入のこと
  • アノテーションを付与することで「このクラスはこうやってインスタンスを作ってね!」とDaggerに教えてあげる
  • あとはDaggerが依存関係の順番通りにインスタンスを作ってくれるぞ!
25
16
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
25
16