2011-08-06 92 views
9

我正在使用Google Guice進行依賴注入。假設我有以下幾點:基於條件的依賴注入

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(); 
    } 
} 

根據這一點,是捆綁,像這樣一個非常簡單的模塊:

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

正如你所看到的,一個Payment實例注入秩序構造。這是在MyModule課上完成的,總體而言非常酷。

我主要的樣子:

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

我卻無法看到的,是我怎麼可能把一些方法來有條件地結合無論是PaymentCardImpl一個PaymentCashImpl實例的Order構造。

比方說,例如,訂單是'在線'訂單。然後我需要:

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

這樣做的最好方法是什麼?我是新來的依賴注入。

+0

@Named("Cash") Payment payment 

或? –

回答

5

你可以註釋你想注入哪一個。如果你做了一個命名綁定,它將解決問題。

見下文:

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

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

然後要注入你做:當你知道你需要_which_實施

@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得到一個例外。我只能做一個綁定。你能舉一個你的意思嗎?我想我可能在這裏錯過了依賴注入的觀點。我正在考慮運行時綁定。 – Joeblackdev

+0

這不是一個好的抽象,但最天真的方式是(編輯後) –

+0

感謝您的例子。當你說「注入所有可能的付款方式」時,你在這裏意味着什麼?謝謝 – Joeblackdev

10

依賴注入對於創建service樣式對象很有用。這些具有以下特點: -

  • 多種實現可能,
  • 重的行爲,
  • 內部狀態僅限於它們的依賴,他們通常是不可變的
  • 將映射到一個演員真實世界(例如收銀員)而不是東西

基於此,Payment是服務對象。我將其重命名爲PaymentService,以將其與您可能存儲的有關付款(這將是一個值對象)的分類帳條目區分開來。

你的例子並沒有顯示什麼Order類,但我認爲它會保存一些行項目,交貨地址和總金額等信息。這是一個value對象。它代表了商業領域中的一件事。

價值對象在狀態上很重,在行爲上較輕。多個實現是可能的,但你不太可能想用另一個替換一個實現。

值對象不是由您的依賴注入框架創建的。它們是由您的業務邏輯代碼創建的。在你的例子中,你使用Guice創建所有的對象。我預計在實際中,您需要根據用戶輸入在運行時創建Order

服務對象可以依賴於值對象,但決不會相反。我想你應該尋找到實現:

checkoutService.payfor(order, method);

,而不是order.finishOrder(method)

CheckoutService類,你可以選擇一個approriate PaymentService並通過order給它。該CheckoutService將採取構造函數的參數PaymentCardPaymentService(相當於你PaymentCardImpl)和CashPaymentService(相當於你PaymentCashImpl)。

+0

很好的答案,謝謝你。在我的實際情況中,我使用的SOAP服務沒有任何內部狀態,因此您所概述的特徵會與我需要的內容產生共鳴。儘管如此,你給了我一些不錯的提示!如果你能提供一個你所概述的工作例子,這也是非常有用的。謝謝 – Joeblackdev

+0

實際上,SOAP服務是什麼?這是付款實施嗎?他們不會有像URL,代理配置等狀態嗎?我不知道Guice,所以我不能提供一個例子,但我認爲你需要先讓你的課程順利進行,然後其他回答者的答案纔有用。 –

3

使用MapBinder擴展名,您可以注入包含在Map中的多個綁定。那麼這是一個使用哪一個實施的問題。

你需要一把鑰匙,你的情況可能是一個StringEnumeration和所有Payment實現綁定到適當的鍵:

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); 
    } 
} 

然後在Order你得到注射全圖,並決定哪些方案中使用:

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是單身人士,而另一人是每次創建的。您可以使用guice scopes來控制壽命,甚至可以實現您自己的提供商來完成其他任務。