Daggerってありますよね。コンパイル時に依存性を解決するのでパフォーマンス的に有利なDIコンテナです。
https://google.github.io/dagger/
依存関係の不備がコンパイル時にエラーになって発見できるのも、実行時にエラーが出たときの修正の難易度が高いAndroidアプリにはありがたいということで、Androidでよく使われてるようです。
基本的なオブジェクトの定義
I have a pen.
public class Pen { @Override public String toString() { return "ペン"; } }
I have an apple.
public class Apple { @Override public String toString() { return "アッポー"; } }
Ohh!!! Apple Pen!!!
public class ApplePen { Pen pen; Apple apple; public ApplePen(Pen pen, Apple apple) { this.pen = pen; this.apple = apple; } @Override public String toString() { return "" + apple + pen; } }
オブジェクトグラフの定義
Daggerでは、オブジェクトの生成を、@Moduleアノテーションがついたクラスの @Providesアノテーションがついたメソッドで定義します。
import dagger.Module; import dagger.Provides; @Module public class Piko { @Provides Pen iHaveAPen() { return new Pen(); } @Provides Apple iHaveAnApple() { return new Apple(); } @Provides ApplePen applePen(Pen pen, Apple apple) { return new ApplePen(pen, apple); } }
applePenメソッドはPenとAppleを引数として受け取りますが、これはDaggerが自動的に設定してくれます。
このようにして、依存関係を定義します。
Daggerでオブジェクト生成
Daggerにオブジェクト生成させることももちろんできます。その場合、コンストラクタにjavax.inject.Injectアノテーションをつけます。
I have pineapple.
import javax.inject.Inject; public class Pineapple { @Inject public Pineapple() { } @Override public String toString() { return "パイナッポー"; } }
Ohhh!!! Pineapple Pen!
import javax.inject.Inject; public class PenPineapple { Pen pen; Pineapple pineapple; @Inject public PenPineapple(Pen pen, Pineapple pineapple) { this.pen = pen; this.pineapple = pineapple; } @Override public String toString() { return "" + pen + pineapple; } }
Ahhh!!! Pen Pineapple Apple Pen!!!
import javax.inject.Inject; public class PenPineappleApplePen { PenPineapple penPineapple; ApplePen applePen; @Inject public PenPineappleApplePen(PenPineapple penPineapple, ApplePen applePen) { this.penPineapple = penPineapple; this.applePen = applePen; } @Override public String toString() { return "" + penPineapple + applePen; } }
コンポーネントの定義
Daggerによって組み立てられたオブジェクトを取得するために、コンポーネントを定義する必要があります。オブジェクトグラフのモジュールを指定した@Componentアノテーションをつけてinterfaceを定義して、その中に必要なオブジェクトを返すメソッドを定義します。
@Component(modules = Piko.class) interface Taro { PenPineappleApplePen ppap(); }
モジュールとコンポーネントは対応することが多いので、モジュールクラスの中で定義すると便利なことが多いです。
import dagger.Component; import dagger.Module; import dagger.Provides; @Module public class Piko { @Component(modules = Piko.class) interface Taro { PenPineappleApplePen ppap(); } @Provides Pen iHaveAPen() { return new Pen(); } @Provides Apple iHaveAnApple() { return new Apple(); } @Provides ApplePen applePen(Pen pen, Apple apple) { return new ApplePen(pen, apple); } }
使ってみる
Daggerでは、コンパイル時にこのようなクラスが生成されます。
コンポーネントに対して、「Dagger」で始まるクラスが生成されています。内部インタフェースの場合は、「_」で区切られてインタフェース名が追加されます。今回は、Pikoクラスの中にTaroインタフェースを定義したので、「DaggerPiko_Taro」になってますね。
ここにBuilerクラスが定義されます。このBuilderクラスには、モジュールの名前のメソッドが定義されているので、そのメソッドを使ってモジュールを設定します。最後にbuildメソッドを呼び出すと、コンポーネントオブジェクトが返ってくるので、定義したメソッドを呼び出して組立済オブジェクトを取得します。
Piko.Taro pikotaro = DaggerPiko_Taro.builder()
.piko(new Piko())
.build();
PenPineappleApplePen ppap = pikotaro.ppap();
ということで、こんな感じになります。
public class PPAP { public static void main(String[] args) { Piko.Taro pikotaro = DaggerPiko_Taro.builder() .piko(new Piko()) .build(); PenPineappleApplePen ppap = pikotaro.ppap(); System.out.println(ppap); } }