带有多种混凝土的果汁......选择其中一种 [英] Guice with multiple concretes......picking one of them

查看:92
本文介绍了带有多种混凝土的果汁......选择其中一种的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在注入相同界面的多个混凝土.

I am injection multiple concretes of the same interface.

我弄清楚了指南中的编码"约定.

I figured out the Guide "code it up" convention.

我的代码当前已退出

[INFO] App - About to ship. (abc)
[INFO] App - ShipperInterface . (FedExShipper)
[INFO] App - ShipperInterface . (UpsShipper)
[INFO] App - ShipperInterface . (UspsShipper)

所以我的指尖有多个托运人".

So I have the multiple "shippers" at my fingertips.

注意方法:

public void ProcessOrder(String preferredShipperAbbreviation,Order ord){

public void ProcessOrder(String preferredShipperAbbreviation, Order ord) {

我正在尝试找出使用(String)preferredShipperAbbreviation选择3个具体托运人中的1个的最佳方法.

I'm trying to figure out the best way to use the (String) preferredShipperAbbreviation to choose 1 of the 3 concrete shippers.

当我在Guice中注册我的3种混凝土时,有没有办法为它们命名"?

Is there a way to "name" my 3 concretes when I register them with Guice?

或者从三个中选择一个的最佳方法是什么?

Or what is the best way to pick 1 of the three ?

public class ProductionInjectModule extends AbstractModule implements Module {

  @Override
  protected void configure() {
    try {
      bind(OrderProcessorInterface.class).toConstructor(OrderProcessorImpl.class.getConstructor(Set.class));

      Multibinder<ShipperInterface> multibinder = Multibinder.newSetBinder(binder(), ShipperInterface.class);
      multibinder.addBinding().toConstructor(FedExShipper.class.getConstructor(org.apache.commons.logging.Log.class));
      multibinder.addBinding().toConstructor(UpsShipper.class.getConstructor(org.apache.commons.logging.Log.class));
      multibinder.addBinding().toConstructor(UspsShipper.class.getConstructor(org.apache.commons.logging.Log.class));

    } catch (NoSuchMethodException e) {
      addError(e);
    }
  }

}

=============

=============

import java.util.Collection;
import java.util.Set;

import org.apache.commons.logging.Log;


public class OrderProcessorImpl implements OrderProcessorInterface {

  private Log logger;
  Set<ShipperInterface> shippers;

  public OrderProcessorImpl(Log lgr, Set<ShipperInterface> shprs) {

    if (null == lgr) {
      throw new IllegalArgumentException("Log is null");
    }

    if (null == shprs) {
      throw new IllegalArgumentException("ShipperInterface(s) is null");
    }

    this.logger = lgr;
    this.shippers = shprs;
  }

  public void ProcessOrder(String preferredShipperAbbreviation, Order ord) {
    this.logger.info(String.format("About to ship. (%1s)", preferredShipperAbbreviation));

    for (ShipperInterface sh : shippers) {
      this.logger.info(String.format("ShipperInterface . (%1s)", sh.getClass().getSimpleName()));
    }

  }
}

=============

=============

public interface OrderProcessorInterface {

  void ProcessOrder(String preferredShipperAbbreviation, Order ord);

}

public class FedExShipper implements ShipperInterface {

  private Log logger;

  public FedExShipper(Log lgr) {

    if (null == lgr) {
      throw new IllegalArgumentException("Log is null");
    }

    this.logger = lgr;
  }

  public void ShipOrder(Order ord) {
    this.logger.info("I'm shipping the Order with FexEx");
  }
}


public class UpsShipper implements ShipperInterface {

  private Log logger;

  public UpsShipper(Log lgr) {

    if (null == lgr) {
      throw new IllegalArgumentException("Log is null");
    }

    this.logger = lgr;
  }

  public void ShipOrder(Order ord) {
    this.logger.info("I'm shipping the Order with Ups");
  }
}


public class UspsShipper implements ShipperInterface {

  private Log logger;

  public UspsShipper(Log lgr) {

    if (null == lgr) {
      throw new IllegalArgumentException("Log is null");
    }

    this.logger = lgr;
  }

  public void ShipOrder(Order ord) {
    this.logger.info("I'm shipping the Order with Usps");
  }
}

..............

..............

主要"方法:

ProductionInjectModule pm = new ProductionInjectModule();
Injector injector = Guice.createInjector(pm);

Order ord = new Order();
OrderProcessorInterface opi = injector.getInstance(OrderProcessorInterface.class);
opi.ProcessOrder("WhatDoIPutHere?", ord);

============ 下面的Guice版本:

=========== Guice version below:

    <dependency>
        <groupId>com.google.inject</groupId>
        <artifactId>guice</artifactId>
        <version>4.2.0</version>
    </dependency>

===============================

================================

我尝试这种方式的一种方式.这和其他方式一样好吗?

One way I'm trying it this way. Is this as good as any way?

最终,在我的真实"场景中(不是这样)……我想将"concreteKey"保留为数据库/配置设置.

Ultimately, in my "real" scenario (not this made up one)......I want to keep the "concreteKey" as a database/configuration setting.

Order ord = new Order();
OrderProcessorInterface opi = injector.getInstance(OrderProcessorInterface.class);
opi.ProcessOrder(FedExShipper.class.getSimpleName(), ord);


  public void ProcessOrder(String preferredShipperAbbreviation, Order ord) {
    this.logger.info(String.format("About to ship. (%1s)", preferredShipperAbbreviation));

    ShipperInterface foundShipperInterface = this.FindShipperInterface(preferredShipperAbbreviation);
    foundShipperInterface.ShipOrder(ord);
  }

  private ShipperInterface FindShipperInterface(String preferredShipperAbbreviation) {

    /* requires java 8 */
    ShipperInterface foundShipperInterface = this.shippers
        .stream().filter(x -> x.getClass().getSimpleName().equalsIgnoreCase(preferredShipperAbbreviation)).findFirst().orElse(null);

    if(null == foundShipperInterface)
    {
      throw new NullPointerException(String.format("ShipperInterface not found in ShipperInterface collection. ('%1s')", preferredShipperAbbreviation));
    }

    return foundShipperInterface;
  }

============= APPEND =================

============= APPEND ==================

感谢Jeff B的回答/评论,使我的工作得以解决.

I got this to work thanks to Jeff B's answer/comments.

import java.util.Map;
import java.util.Set;

import com.google.inject.AbstractModule;
import com.google.inject.Module;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.Multibinder;

public class ProductionInjectModule extends AbstractModule implements Module {

  @Override
  protected void configure() {
    try {

      MapBinder<String, ShipperInterface> mappyBinder = MapBinder.newMapBinder(binder(), String.class, ShipperInterface.class);
      mappyBinder.addBinding("myFedExName").toConstructor(FedExShipper.class.getConstructor(org.apache.commons.logging.Log.class));
      mappyBinder.addBinding("myUPSName").toConstructor(UpsShipper.class.getConstructor(org.apache.commons.logging.Log.class));
      mappyBinder.addBinding("myUSPSName").toConstructor(UspsShipper.class.getConstructor(org.apache.commons.logging.Log.class));

        /* below is not needed, but shows what needs to be injected */
      java.util.Map<String,  javax.inject.Provider<ShipperInterface>> shipperProviderMap;


    } catch (NoSuchMethodException e) {
      addError(e);
    }
  }
}

================

================

import java.util.Collection;
import java.util.Set;

import org.apache.commons.logging.Log;

public class OrderProcessorImpl implements OrderProcessorInterface {

  private Log logger;
  private java.util.Map<String, javax.inject.Provider<ShipperInterface>> shipperProviderMap;

  public OrderProcessorImpl(Log lgr, java.util.Map<String, javax.inject.Provider<ShipperInterface>> spMap) {

    if (null == lgr) {
      throw new IllegalArgumentException("Log is null");
    }

    if (null == spMap) {
      throw new IllegalArgumentException("Provider<ShipperInterface> is null");
    }

    this.logger = lgr;
    this.shipperProviderMap = spMap;
  }

  public void ProcessOrder(String preferredShipperAbbreviation, Order ord) {
    this.logger.info(String.format("About to ship. (%1s)", preferredShipperAbbreviation));


    ShipperInterface foundShipperInterface = this.FindShipperInterface(preferredShipperAbbreviation);
    foundShipperInterface.ShipOrder(ord);
  }

  private ShipperInterface FindShipperInterface(String preferredShipperAbbreviation) {

    ShipperInterface foundShipperInterface = this.shipperProviderMap.get(preferredShipperAbbreviation).get();

    if (null == foundShipperInterface) {
      throw new NullPointerException(
          String.format("ShipperInterface not found in shipperProviderMap. ('%1s')", preferredShipperAbbreviation));
    }

    return foundShipperInterface;
  }
}

================

================

主要"方法

ProductionInjectModule pm = new ProductionInjectModule();
Injector injector = Guice.createInjector(pm);

Order ord = new Order();
OrderProcessorInterface opi = injector.getInstance(OrderProcessorInterface.class);
opi.ProcessOrder("myFedExName", ord); /* now use the "friendly named" strings */

输出:

[INFO] App - About to ship. (myFedExName)
[INFO] App - I'm shipping the Order with FexEx

我可能在新发布的代码中添加了一些额外的记录程序" .....但是简单的清理就可以使其运行.

I probably have some extra "logger" injections in my newly posted code.....but simple clean up would get it running.

推荐答案

如果将Multibinder用于 map 绑定,则可以使用

If you use Multibinder for map bindings, then you could bind each of the Shipper instances into a Map using MapBinder:

MapBinder<String, ShipperInterface> multibinder = MapBinder.newMapBinder(
    binder(), String.class, ShipperInterface.class);
multibinder.addBinding("FedEx").to(FedExShipper.class);
multibinder.addBinding("UPS").to(UpsShipper.class);
multibinder.addBinding("USPS").to(UspsShipper.class);

然后在注入的类中可以注入Map<String, Provider<ShipperInterface>>:

Then in your injected class you can inject a Map<String, Provider<ShipperInterface>>:

private ShipperInterface FindShipperInterface(String 
    preferredShipperAbbreviation) {

  ShipperInterface foundShipperInterface =
      providerMap.get(preferredShipperAbbreviation).get();
}

您也可以直接注入Map<String, ShipperInterface>,但是Multibinder是免费处理Provider间接寻址的,这使您避免创建三个ShipperInterface实例,而实际上只需要一个实例.另外,如果您的实例选择代码比仅仅基于在编译时知道的一组实现中的字符串选择更为复杂,则您可能仍需要您编写的Factory实现.

You could also inject a Map<String, ShipperInterface> directly, but Multibinder handles the Provider indirection for free, which lets you avoid creating three ShipperInterface instances when only one will actually be necessary. Also, if your instance-selection code is more complicated than simply choosing based on a String from a set of implementations you know at compile time, you might still want a Factory implementation you write.

作为旁注,理想情况下使用@Inject注释和bind(...).to(...)代替 .这不会将您绑定到Guice,因为@Inject是在JSR-330中定义的,并且您正在添加注释,可以选择以后不使用.您也可以像这样在您的AbstractModule中编写一个@Provides方法,该方法并不比您的toConstructor绑定更脆弱:

As a side note, ideally use @Inject annotations and bind(...).to(...) instead of toConstructor. This doesn't tie you to Guice, because @Inject is defined in JSR-330, and you are adding annotations that you can choose not to use later. You can also write a @Provides method in your AbstractModule, like so, which is no more fragile than your toConstructor bindings:

@Provides UspsShipper provideUspsShipper(Log log) {
  return new UspsShipper(log);
}

仅当您使用旧代码,不受控制的代码,限制性很强的代码样式规则或AOP时才使用toConstructor.为了简化示例,我已经在上面进行了此操作,但是如果需要,您可以还原为toConstructor.

Use toConstructor if and only if you are using legacy code, code you don't control, very restrictive code style rules, or AOP (which may be the case here). I've done so above for the sake of a concise example, but you can revert to toConstructor if necessary.

这篇关于带有多种混凝土的果汁......选择其中一种的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆