I am injection multiple concretes of the same interface.
I figured out the Guide "code it up" convention.
My code currently spits out
[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.
Note the method:
public void ProcessOrder(String preferredShipperAbbreviation, Order ord) {
I'm trying to figure out the best way to use the (String) preferredShipperAbbreviation to choose 1 of the 3 concrete shippers.
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");
}
}
..............
"Main" method:
ProductionInjectModule pm = new ProductionInjectModule();
Injector injector = Guice.createInjector(pm);
Order ord = new Order();
OrderProcessorInterface opi = injector.getInstance(OrderProcessorInterface.class);
opi.ProcessOrder("WhatDoIPutHere?", ord);
=========== 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?
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 ==================
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;
}
}
================
"main" method
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 */
OUTPUT:
[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.