Android-CleanArchitecture とか読んでいると、当たり前のようにDaggerが使われていまして。
初見じゃ読めない!!
コードを読み解くために、まずはDagger(正確には Dagger2)を学んでみることにしました。
どうしてDIが重要か
メンテナンスとテストが楽になるからです。以上!
というくらいの認識で良いんじゃないでしょうか。
こちらのブログ記事がわかりやすかったので、一読をおすすめします。
Dagger で使うアノテーションの役割
Dagger 2 のサイト を上から眺めてみた感じです。
@Inject "注入箇所"
コンストラクタにつける場合とフィールドにつける場合がある。
コンストラクタの場合は引数に合わせて、フィールドの場合は型に合わせて、必要なインスタンスを生成して注入してくれる。
Daggerではメソッド注入(setterを通しての注入)はサポートしていない。
@Provides "注入物生成メソッド" と @Module "Providesのまとめ役"
@Inject では、指定された型がClassであれば、自動的にインスタンスを生成してそこに注入してくれるっぽい。
通常、DIではinterfaceを使うことが多いので、 @provides は「@inject に対応する注入物を生成するメソッド」だと思えばいい。
@Provides は @Module に属している必要がある。
@Inject が車の給油口だとしたら、 @Module はガソリンスタンド、 @Provides はスタンドに設置されている給油機にあたる感じ。
@Prodives 指定できるのは名前が provide で始まるメソッドのみで、
@Module 指定できるのは名前が Module で終わるクラスのみ。
@Component(modules = SomeModule) "ここが依存関係グラフの頂点"
これがイマイチよくわからない。
実際に注入を行うためのトリガーを叩くため、依存関係グラフの頂点を指定するらしい。
@Component 指定できるのは得たい型を返す引数なしのメソッドを持ったinterfaceのみ。
すると、実体クラスが Dagger + ↑のinterface名 という名前で生成される。
interface名が SomeComponent だったらば、生成される実体クラス名は DaggerSomeComponent 。
生成された実体クラスは Builder クラスを持っていて、 builder() で取得できる。
Builder に Module の実体を与えてから build() すれば、 interface で用意したメソッドが注入をすべて済ませたインスタンスを返してくれる。
手動で Module の実体を与える必要がない場合は、 Builder を使わなくても create() メソッドで注入が済んだインスタンスを取得できる。
@Provides + @Singleton "シングルトンとして、返り値は使い回す"
@Provides 指定されたメソッドがシングルトン化するイメージ。
実際に依存関係が解決されて注入が行われるときに、初めて @Provides が返した値が使いまわされる、という理解でいいのだろうか。良さそう。
クラスに @Singleton を付けても何の効力もないが、「このクラスは複数のスレッドにまたがって使われ得る」という印として使われる。Javadocにもその旨が記載されて便利。
ジョセフの血をDIOに吸わせたり、ジョセフに戻したりする感じ。
@Inject Lazy<T> "T型のインスタンスを遅延注入"
インスタンスの生成を遅延することができる。
@Inject の箇所に実際に注入されてくるのは Lazy<T> のインスタンス。
ちなみに Lazy はDaggerに含まれているinterface。
get() メソッドで T のインスタンスを生成・取得できる。
@Named "@Inject と @Provides の対応を明示的にする"
"interfaceは同じだけれども違うクラス" を注入したいとか、自動的に注入する側とされる側の関係が解決できないときとかに使用。
注入される側 @Inject と 注入する側 @Provides 両方に同じ修飾子を書く必要がある。
@Inject @Named("hoge") SomeType mHoge;
@Inject @Named("fuga") SomeType mFuga;
@Provides @Named("hoge") SomeType provideHoge(){ ... }
@Provides @Named("fuga") SomeType provideFuga(){ ... }
実は、重要なのは @Named ではなく @Qualifier アノテーションが付いたアノテーション(ややこしい!)。
@Named には最初から @Qualifier が付いているのでこの用途に使える。
自分でアノテーションを作っても良い。
@Module(overrides = true) "このモジュールを優先して使う"
競合する @Provides が存在した場合、 @Module(overrides = true) と書かれている Module の方が優先的に使われる。
テストの時、モックを注入して使ったりするのに便利。
なんとなくわかった気になったので、今度から使ってみます。˚✧₊⁎( ˘ω˘ )⁎⁺˳✧༚