Can Guice automatically create instances of different classes based on a parameter?

A standard factory object might look like this:

interface I { ... } class A implements I { ... } class B implements I { ... } class IFactory { I getI(int i) { switch (i) { case 1: return new A(); default: return new B(); } } } 

Is it possible to configure the bindings so that the transition is completed for me, i.e. all i do is getInstance call or input? I was looking for an auxiliary injection, but this is apparently a different topic: https://code.google.com/p/google-guice/wiki/AssistedInject

+4
source share
1 answer

It looks like you're looking for a MapBinder , which is part of Multibindings . Note that you still need to insert some kind of IFactory or another factory interface, because getInstance does not accept the parameter as your getI , and you still need to set the mapping from integers to the class implementation.

MapBinder style

 class IModule extends AbstractModule { @Override public void configure() { MapBinder<Integer, I> myBinder = MapBinder.newMapBinder(binder(), Integer.class, I.class); myBinder.addBinding(1).to(A.class); // Add more here. } } // You can even split the MapBinding across Modules, if you'd like. class SomeOtherModule extends AbstractModule { @Override public void configure() { // MapBinder.newMapBinder does not complain about duplicate bindings // as long as the keys are different. MapBinder<Integer, I> myBinder = MapBinder.newMapBinder(binder(), Integer.class, I.class); myBinder.addBinding(3).to(C.class); myBinder.addBinding(4).to(D.class); } } 

An injector configured with these modules will provide an injectable Map<Integer, I> , which has an instance of everything connected; here it will be a three-dimensional map from 1 to a fully injected instance of A , from 3 to a C and from 4 to a D This is actually an improvement over your switch example, which used the new keyword and therefore did not introduce any dependencies in A or B

For a better option that does not create so many running instances, enter Map<Integer, Provider<I>> , which MapBinder also provides automatically. Use it like this:

 class YourConsumer { @Inject Map<Integer, Provider<I>> iMap; public void yourMethod(int iIndex) { // get an I implementor I i = iMap.get(iIndex).get(); // ... } } 

To provide a default implementation (and an opaque interface), as you did, you will want to implement your own short shell on top of the MapBinder :

 class IFactory { @Inject Map<Integer, Provider<I>> iMap; @Inject Provider<B> defaultI; // Bound automatically for every Guice key I getI(int i) { return iMap.containsKey(i) ? iMap.get(i).get() : defaultI.get(); } } 

Simplified, factory style

If the above seems redundant, remember that you can enter Injector and create a local Map from the key to the implementation, (You can also use ImmutableMap , as I here).

 class IFactory { @Inject Injector injector; // This is a bad idea, except for times like this @Inject Provider<B> defaultI; static final ImmutableMap<Integer, Class<? extends I>> map = ImmutableMap.of( 1, A.class, 3, C.class, 4, D.class); I getI(int i) { return map.containsKey(i) ? injector.getInstance(map.get(i)) : defaultI.get(); } } 
+13
source

Source: https://habr.com/ru/post/1491577/


All Articles