AssistedInject
人手によるファクトリ
コンストラクタの一部のパラメータをGuiceによる注入とし、その他のパラメータを呼び出し側から指定したいことがある。
public class RealPayment implements Payment { public RealPayment( CreditService creditService, // インジェクタから AuthService authService, // インジェクタから Date startDate, // 呼び出し側から Money amount); // 呼び出し側から } ... }
この問題の標準的な解決法Guiceがオブジェクトをビルドするのを助けるファクトリを記述することである。
public interface PaymentFactory { public Payment create(Date startDate, Money amount); } public class RealPaymentFactory implements PaymentFactory { private final Provider<CreditService> creditServiceProvider; private final Provider<AuthService> authServiceProvider; @Inject public PaymentFactory(Provider<CreditService> creditServiceProvider, Provider<AuthService> authServiceProvider) { this.creditServiceProvider = creditServiceProvider; this.authServiceProvider = authServiceProvider; } public Payment create(Date startDate, Money amount) { return new RealPayment(creditServiceProvider.get(), authServiceProvider.get(), startDate, amount); } }
そしてモジュール内では対応するバインディングを記述する。
bind(PaymentFactory.class).to(RealPaymentFactory.class);
このような状況が現れるその度に、ボイラープレートな(決まりきった)ファクトリクラスを書くのは面倒である。 なおかつ、実装クラスの依存が変更されるとファクトリも更新しなくてはならないのもまた面倒である。
AssistedInjectによるファクトリ
AssistedInjectじゃファクトリクラスの実装を自動的に生成する。 使用するには、実装クラスのコンストラクタとインジェクタの知らないフィールドをアノテーションする。
public class RealPayment implements Payment { @Inject public RealPayment( CreditService creditService, AuthService authService, @Assisted Date startDate, @Assisted Money amount); } ... }
そして、モジュールの中でProvider<Factory>をバインドする。
bind(PaymentFactory.class).toProvider( FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));
FactoryProviderはcreate()メソッドのパラメータを、実装クラスのコンストラクタ中の対応する@Assistedパラメータにマップする。 他のコンストラクタ引数については、通常のインジェクタにその値を問い合わせる。
FactoryProviderを使えば、コンストラクタの追加引数を必要とするクラスの生成が簡単になる。
実装クラス(RealPaymentのような)のコンストラクタとassistedパラメータをアノテーションする。
- assistedパラメータのみを引数とするcreate()メソッドのあるファクトリインターフェースを定義する。ただし、それらの引数順序はコンストラクタのものと同一であること。
FactoryProviderを使って、ファクトリをプロバイダにバインドする。