Joe*_*dev 9 java dependency-injection guice
我正在使用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();
}
}
Run Code Online (Sandbox Code Playgroud)
接下来,这是一个非常简单的绑定模块,如下所示:
public class MyModule extends AbstractModule {
@Override
protected void configure() {
bind(Payment.class).to(PaymentCashImpl.class);
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,Payment实例被注入Order构造函数中.这是在MyModule课堂上完成的,整体来说非常酷.
我的主要看起来像:
public static void main(String[] args) {
MyModule module = new MyModule();
Injector injector = Guice.createInjector(module);
Order order = injector.getInstance(Order.class);
order.finishOrder();
}
Run Code Online (Sandbox Code Playgroud)
然而,我无法看到的是如何通过某种方式将一个PaymentCardImpl 或一个PaymentCashImpl实例有条件地绑定到Order构造函数.
比方说,例如,订单是"在线"订单.我需要这个:
bind(Payment.class).to(PaymentCardImpl.class);
Run Code Online (Sandbox Code Playgroud)
最好的方法是什么?我是依赖注入的新手.
WW.*_*WW. 11
依赖注入对于创建service样式对象很有用.它们具有以下特点: -
基于此,Payment是一个服务对象.我会将其重命名,PaymentService以区别于您可能存储的有关付款的分类帐条目(这将是一个价值对象).
您的示例并未显示Order该类的内容,但我认为它会包含某些行项目,传递地址和总金额等信息.这是一个value对象.它代表了业务领域的一件事.
值对象在状态上很重,在行为上更轻.可以实现多种实现,但您不太可能希望将一种实现替换为另一种实现.
值依赖注入框架不会创建值对象.它们由您的业务逻辑代码创建.在您的示例中,您使用Guice创建所有对象.我希望实际上你需要Order根据用户输入创建at运行时.
服务对象可以依赖于值对象,但从不相反.我认为你应该寻求实施:
checkoutService.payfor( order, method );
并不是 order.finishOrder( method )
在CheckoutService课堂上,你可以选择一个approriate PaymentService并传递order给它.该CheckoutService会采取构造函数的参数PaymentCardPaymentService(相当于你PaymentCardImpl)和CashPaymentService(相当于你PaymentCashImpl).
我知道你为什么要这样做.但我不会将构造代码(依赖注入配置)与业务逻辑混淆.如果这样做,您的业务逻辑可能不再可理解.对我来说,似乎你的条件注入取决于情况,即来自用户界面的输入.
那么为什么不注入两者并使条件明确?我更喜欢那个.一个示例应用:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
还是为了你自己的练习:付款的东西.你不需要任何Guice代码.剩下的工作由Guice自动完成.如果您开始使用接口,则可以开始使用此处所述的绑定:http://code.google.com/p/google-guice/wiki/GettingStarted.但是,如果您没有任何接口,则无需任何配置即可完成构建.@Inject注释就足够了.
当然这只是一个示例设计.但它显示了使用Guice设置一个很好的解耦Java应用程序是多么容易.
使用MapBinder扩展,您可以注入包含在Map. 然后是使用哪个实现的问题。
您将需要一个密钥,在您的情况下,它可能是一个StringorEnumeration并将所有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);
}
}
Run Code Online (Sandbox Code Playgroud)
然后在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();
}
}
Run Code Online (Sandbox Code Playgroud)
注意:注入提供程序不适用于javax.inject.Provider,您需要使用com.google.inject.Provider.
通过注入提供者而不是实例,您可以实现延迟实例化和每个实现不同的实例化策略。就像上面的例子一样,PaymentCardImpl是单例,而另一个是每次创建的。您可以使用 guice范围来控制生命周期,甚至可以实现自己的提供程序来完成其他事情。
您可以注释要注入的是哪一个.如果您执行命名绑定,它将解决该问题.
见下文:
bind(Payment.class).annotatedWith(Names.named("Card")).to(PaymentCardImpl.class);
bind(Payment.class).annotatedWith(Names.named("Cash")).to(PaymentCashImpl.class);
Run Code Online (Sandbox Code Playgroud)
那么你要注射的地方是:
@Named("Cash") Payment payment
Run Code Online (Sandbox Code Playgroud)
要么:
@Named("Card") Payment payment
Run Code Online (Sandbox Code Playgroud)