2011-08-06 6 views
9

依存性注入にGoogle Guiceを使用しています。あなたが見ることができるように条件に基づく依存性注入

public class MyModule extends AbstractModule { 
    @Override 
    protected void configure() { 
     bind(Payment.class).to(PaymentCashImpl.class); 
    } 
} 

Paymentインスタンスは注文のコンストラクタに注入されています

public interface Payment { 
    public void pay(); 
} 

public class PaymentCardImpl implements Payment { 
    public void pay() { 
     System.out.println("I pay with a card"); 
    } 
} 

public class PaymentCashImpl implements Payment { 
    public void pay() { 
     System.out.println("I pay cash"); 
    } 
} 

public class Order { 

    private Payment payment; 

    @Inject 
    public Order(Payment payment){ 
     this.payment=payment; 
    } 

    public void finishOrder(){ 
     this.payment.pay(); 
    } 
} 

このに続き、そうのように、結合のための非常にシンプルなモジュールがある:私は次があるとし。これはMyModuleクラスで行われ、全体的には本当にクールです。

私の主なルックスは次のように:

私はしかし、見ることができない何
public static void main(String[] args) { 
    MyModule module = new MyModule(); 
    Injector injector = Guice.createInjector(module); 
    Order order = injector.getInstance(Order.class); 
    order.finishOrder(); 
} 

、私は条件付きでOrderコンストラクタにPaymentCardImplまたはPaymentCashImplインスタンスのいずれかと結合するいくつかの方法を組み込むことができる方法です。

たとえば、注文は「オンライン」注文だったとしましょう。私はこの必要があります:

bind(Payment.class).to(PaymentCardImpl.class); 

これを行うための最善の方法は何ですか?私は依存症注射が初めてです。

+0

あなたが必要な_which_実装を知っていますいつですか? –

答えて

5

注入する注釈を付けることができます。名前付きバインドを行うと、問題が解決されます。

は、以下を参照してください。

bind(Payment.class).annotatedWith(Names.named("Card")).to(PaymentCardImpl.class); 

bind(Payment.class).annotatedWith(Names.named("Cash")).to(PaymentCashImpl.class); 

を次にあなたがやる注入する場所:

@Named("Cash") Payment payment 

か:

@Named("Card") Payment payment 
+0

+1これは私が最後に使ったアプローチなので、これを最善の答えとしてマークします。それは私に最高の柔軟性をもたらしました。 – Joeblackdev

8

私はあなたがこれをしたい理由を知っています。しかし、私は建設コード(依存性注入構成)とビジネスロジックを混ぜ合わせません。そうすると、ビジネスロジックがもう理解できなくなる可能性があります。私の考えでは、条件付き注入は状況、すなわちユーザーインターフェイスからの入力に依存します。

なぜ、両方を注入して条件を明示的にしないのはなぜですか?私はそれを好むだろう。例のアプリ:

public class MyModule extends AbstractModule { 
    @Override 
    protected void configure() { 
    } 

    public static void main(String[] args) { 
    MyModule module = new MyModule(); 
    Injector injector = Guice.createInjector(module); 
    Order order = injector.getInstance(Order.class); 
    order.finishOrder(PaymentMethod.CARD); 
    } 
} 

public class PaymentProvider { 
    private final Payment cashPayment, cardPayment; 

    @Inject 
    public PaymentProvider(CardPayment cardPayment, CashPayment cashPayment) { 
    this.cardPayment = cardPayment; 
    this.cashPayment = cashPayment; 
    } 

    public Payment getPaymentByMethod(PaymentMethod method) { 
    switch (method) { 
     case CARD: 
     return cardPayment; 
     case CASH: 
     return cashPayment; 
     default: 
     throw new IllegalArgumentException("Unkown payment method: " + method); 
    } 
    } 
} 

public enum PaymentMethod { CASH, CARD } 

public class Order { 
    private final PaymentProvider paymentProvider; 

    @Inject 
    public Order(PaymentProvider paymentProvider) { 
    this.paymentProvider = paymentProvider; 
    } 

    public void finishOrder(PaymentMethod method) { 
    paymentProvider.getPaymentByMethod(method).pay(); 
    } 
} 

まだお支払いのもの: Guiceコードは必要ありません。残りはGuiceによって自動的に行われます。インターフェイスを使用し始める場合は、http://code.google.com/p/google-guice/wiki/GettingStartedで説明されているようにバインディングを使用し始めます。しかし、もしあなたがどんなインターフェースも持っていなければ、構成は何の構成もなしに行われます。 @Injectアノテーションで十分です。

もちろんこれは単なる設計例です。しかしGuiceを使ってうまく分離されたJavaアプリケーションをセットアップするのがどれほど簡単かを示しています。

+0

両方を注入することはできません。私がこれを試してみると、Guiceから例外が出ます。私は1つのバインディングしか行うことができません。あなたは何を意味するのかの例を教えてくれますか?私はここで依存性注入のポイントを見逃しているかもしれないと思う。私は実行時バインディングを考えています。 – Joeblackdev

+0

これは良い抽象ではありませんが、最も素朴な方法はこれです(編集後) –

+0

ありがとうございます。 「可能なすべての支払方法を注入する」と言うと、ここでは正確に何を意味していますか?ありがとう – Joeblackdev

10

依存性注入は、serviceスタイルオブジェクトを作成するのに便利です。これらは、次のような特徴があります -

  • 複数の実装がそうである、行動上の
  • が重く、
  • 内部状態がその依存関係に限定されており、それらは一般
  • が出演にマッピングうではない変更可能です

これに基づいて、Paymentはサービスオブジェクトです。私はそれをPaymentServiceという名前に変更して、支払い(値オブジェクトとなる)について保存する元帳エントリと区別します。

この例では、Orderクラスの内容はあまり表示されませんが、一部の広告申込情報、配信先住所、合計金額などの情報が保持されると想定しています。これはvalueオブジェクトです。ビジネスドメイン内のものを表します。

値オブジェクトは状態が重く、動作は軽いです。複数の実装が可能ですが、ある実装を別の実装に置き換える可能性は低くなります。

値オブジェクトは依存性注入フレームワークによって作成されません。それらはビジネスロジックコードによって作成されます。あなたの例では、Guiceを使ってすべてのオブジェクトを作成しています。実際には、ユーザー入力に基づいて実行時にOrderを作成する必要があります。

サービスオブジェクトは値オブジェクトに依存することができますが、逆の方法ではありません。あなたがapproriate PaymentServiceを選択し、それにorderを渡すことができ、

checkoutService.payfor(order, method);

なくorder.finishOrder(method)

CheckoutServiceクラス内:私はあなたが実装するために探してされるべきだと思います。 CheckoutServiceは、コンストラクター引数PaymentCardPaymentService(PaymentCardImplに相当)とCashPaymentService(PaymentCashImplに相当)を取ります。

+0

偉大な答え、これはありがとう。実際のシナリオでは、内部状態のないSOAPサービスを使用しているため、概要を示す特性が必要なものと共鳴します。それにもかかわらず、あなたは私に素晴らしいヒントをくれました!あなたが概説したものの実例を提供することができれば、これも非常に役に立つでしょう。ありがとう – Joeblackdev

+0

実際にSOAPサービスとは何ですか?支払いの実装ですか?彼らはURLやプロキシ設定などの状態を持っていないのでしょうか?私はGuiceを知らないので、例を挙げることはできませんが、まずあなたのクラスを真っ直ぐにしてから、他の回答者の回答が役に立つと思う。 –

3

MapBinderエクステンションを使用すると、Mapに含まれるいくつかのバインディングを注入することができます。それで、使用する実装の問題です。

あなたはStringEnumerationことと適切なキーにすべてPayment実装を結合することができ、あなたの場合には、キーが必要になります。そして、Orderであなたがマップ全体を注入します

public class MyModule extends AbstractModule { 
    @Override 
    protected void configure() { 
    // this one aggregates all bindings and could be further annotated if you 
    // need several maps with same key/value types 
    MapBinder<String, Payment> mapBinder = MapBinder.newMapBinder(binder(), 
     String.class, Payment.class); 

    // simple binding of PaymentCashImpl to 'cash' key 
    mapBinder.addBinding("cash").to(PaymentCashImpl.class); 

    // you can scope during binding or using @Singleton annotation on implementations 
    mapBinder.addBinding("card").to(PaymentCardImpl.class).in(Singleton.class); 
    } 
} 

をしているかを決定使用するための実装:

public class Order { 
    @Inject 
    Map<String, Provider<Payment>> paymentProcessors; 

    public void finishOrder(String paymentMethod) { 
    if (!paymentProcessors.containsKey(paymentMethod)) { 
     throw new IllegalArgumentException("Unknown payment method " + paymentMethod); 
    } 
    Payment paymentProcessor = paymentProcessors.get(paymentMethod).get(); 

    // do your stuff... 
    paymentProcessor.pay(); 
    } 
} 

注:注入プロバイダがjavax.inject.Providerでは動作しません、あなたが必要com.google.inject.Providerを使用します。

インスタンスの代わりにプロバイダを追加することで、実装ごとに遅延インスタンス化と異なるインスタンス化ポリシーを実現できます。上記の例のように、PaymentCardImplはシングルトンで、もう1つは毎回作成されます。 guice scopesを使用して寿命を制御したり、独自のプロバイダを実装して他の目的を達成することもできます。