2013-04-16 8 views
26

現在、私はDaggerを使用するためにAndroidのアプリを再設計しています。私のアプリは大きく複雑ですが、最近私は次のシナリオに遭遇しました:コンバーターへの依存性注入にDaggerを使用する

オブジェクトAは、注入のための完璧な候補である特別なDebugLoggerインスタンスを必要とします。ロガーの周りを回るのではなく、Aのコンストラクターを介して注入することができます。これは次のようになります。

class A 
{ 
    private DebugLogger logger; 

    @Inject 
    public A(DebugLogger logger) 
    { 
     this.logger = logger; 
    } 

    // Additional methods of A follow, etc. 
} 

これまでのところ、これは意味があります。しかし、Aは非常に物事のダガーの方法を、以下、Aの複数のインスタンスを構築しなければならない別のクラスBによって構築される必要があり、私は、単純なBにProvider<A>を注入:

class B 
{ 
    private Provider<A> aFactory; 

    @Inject 
    public B(Provider<A> aFactory) 
    { 
     this.aFactory = aFactory; 
    } 
} 

[OK]を、良い今のところ。しかし、突然、Aはその構成に不可欠な「量」という整数などの追加入力が必要です。今、私のAのコンストラクタは次のようになります:

@Inject 
public A(DebugLogger logger, int amount) 
{ 
... 
} 

突然この新しいパラメータが注入を妨げます。さらに、これがうまくいっても、私が間違っていない限り、プロバイダーから新しいインスタンスを取得するときに「量」を渡す方法はありません。私がここでできることはいくつかあり、私の質問はどれが最高ですか?

コンストラクタの後に呼び出されると予想されるsetAmount()メソッドを追加して、Aをリファクタリングすることができました。しかし、私はAの構築を「量」が埋まるまで遅らせる必要があるので、これは醜いです。私がこのような2つのパラメータ、「量」と「頻度」を持っていたら、2人の設定者がいて、複雑な両方のセッターが呼ばれた再開後にAの建設を確保するためのチェック、または私はそうのように、ミックスにさらに第3の方法を追加する必要があります:

(Somewhere in B): 

A inst = aFactory.get(); 
inst.setAmount(5); 
inst.setFrequency(7); 
inst.doConstructionThatRequiresAmountAndFrequency(); 

他の代替は、私は、コンストラクタを使用していないということですベースの注入とフィールドベースの注射と一緒に行く。しかし今、自分のフィールドを公開する必要があります。私のクラスの内部データを他のクラスに明らかにする義務があるので、これは私とうまくはまりません。

はこれまでのところ、私は考えることができる唯一のややエレガントな解決策はそうのように、プロバイダのフィールドベースの注入を使用することです:

class A 
{ 
    @Inject 
    public Provider<DebugLogger> loggerProvider; 
    private DebugLogger logger; 

    public A(int amount, int frequency) 
    { 
     logger = loggerProvider.get(); 
     // Do fancy things with amount and frequency here 
     ... 
    } 
} 

それでも、私は「以来、私は、タイミングがわかりませんよ私のコンストラクタが呼び出される前にDaggerがプロバイダを注入するかどうかはわかりません。

良い方法がありますか? Daggerの仕組みについて何か不足していますか?

答えて

49

あなたが話していることは、アシスト注入と呼ばれています。現在、Daggerでは自動的にサポートされていません。

あなたは工場出荷時のパターンでこれを回避することができます:あなたが作成する必要がある場合に

class B { 
    @Inject AFactory aFactory; 

    //... 
} 

をして:

class AFactory { 
    @Inject DebugLogger debuggLogger; 

    public A create(int amount, int frequency) { 
    return new A(debuggLogger, amount); 
    } 
} 

は今、あなたはこの工場を注入し、Aのインスタンスを作成するためにそれを使用することができますAに 'amount'と 'frequency'を入力して工場を使用します。

A a = aFactory.create(amount, frequency); 

依然としてロガーインスタンスを提供するために、注入を使用しながらAfinalロガーの例、量、および頻度フィールドを有するようにするためにこれが可能となります。

Guiceには、基本的にこれらの工場の作成を自動化する補助注入プラグインがあります。 Daggerメーリングリストのhave been discussionには、それらを追加するための適切な方法が記載されていますが、この執筆時点では何も決定されていません。

+0

ありがとうございました。あなたが記述した工場のパターンは最良のアプローチのようです。ダガーがプライベートフィールドインジェクションをサポートしていれば、私が達成しようとしているものを簡単に許可することができます。それがなぜダガーのオリジナルデザインの一部ではないのだろうか?私はダガーが反射を使って注入すると仮定します。もしそうなら、プライベートフィールドは問題ないでしょうか? – Alex

+0

ダガーは反射に落ちますが、それは注射の主要な手段ではありません。フィールドを直接設定するコードを生成するか、コンストラクタを呼び出します。したがって、ソースツリーの他のコードと同様に動作し、 'private'メンバーにアクセスすることはできません。 –

+0

セキュリティ管理者の中には、クラスのアクセシビリティの変更に依存するリフレクションを中断するものがあります。 –

3

Jakeの投稿は完全に真実です。それはGuiceやDaggerと一緒に仕事をしているGoogleの人々の中には、GuiceやDaggerやスタンドアローンで使用できる「補助注入」や自動工場生成の代替バージョンがあります。つまり、あなたのための工場クラスのソースコードを生成します。これらのファクトリクラスは、標準のJSR-330クラスと同様に(必要に応じて)注入可能です。しかし、それはまだリリースされていません。

このような解決策が出るまで、Jake Whartonのアプローチが推奨されます。

+0

ええ、心配しないで、私はダガーがまだ初期のバージョンであることを認識します。私は、これらの他の機能のいくつかは、バイナリの全体的なサイズを維持するためのプラグインとして開発されていることを願っています(これは、少なくとも私にとってはAndroid開発にとって魅力的なオプションです)。 – Alex

+1

このようにして自動ファクトリ生成:https://github.com/google/auto/tree/master/factory – phazei

3

あなたのコンストラクタに注射液と注射液を混ぜているため、問題があります。あなたの心痛のトンを保存し、クリーンなコードを続ける注射のための一般的なルールは以下のとおりです。

  1. 注射剤は、そのコンストラクタではなく、newablesのために他の注射剤を求めることができます。

  2. Newablesは、コンストラクタで他のnewablesを要求することができますが、注入可能ではありません。

注射剤は、サービスタイプのオブジェクトである、などCreditCardProcessor、MusicPlayer、として働くんつまりオブジェクトが

Newablesは、などのCreditCard、歌、など値型のオブジェクトである

0

ジェイクのポスト素晴らしいですが、もっと簡単な方法があります。 Googleはコンパイル時に工場を自動的に作成するためにAutoFactoryライブラリを作成しました。その後、ライブラリはコンパイル時にAFactoryクラスを作成します

@AutoFactory 
public class A { 

    private DebugLogger logger; 

    public A(@Provided DebugLogger logger, int amount, int frequency) { 
     this.logger = logger; 
    } 
} 

まず、@AutoFactory注釈と引数を注入するための@ProvidedアノテーションでクラスAを作成します。だから、Bクラスのコンストラクタにファクトリを挿入するだけです。

public class B { 

    private final AFactory aFactory; 

    @Inject 
    public B(AFactory aFactory) { 
     this.aFactory = aFactory; 
    } 

    public A createA(int amount, int frequency) { 
     return aFactory.create(amount, frequency); 
    } 
} 
関連する問題