Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing parameterized factory with Guice

I have a factory I like to reimplement using Guice:

enum MyObjects { OBJECT1, OBJECT2, ... }
class Object1 implements SomeInterface { ... }
class Object2 implements SomeInterface { ... }
...
class Factory {
  public static SomeInterface createObject(MyObjects obj) {
    switch (obj) {
    case OBJECT1: return new Object1();
    case OBJECT2: return new Object2();
    ...
    }
  }

Is there an easy way to implement it? Something like Provider.get(parameter) and using bindings to define which object should be used in each case?

like image 257
Geno Roupsky Avatar asked Jun 16 '11 14:06

Geno Roupsky


2 Answers

You have several options here.

1. since you use an enum to distinguish between implementations, then you have a finite number of implementations that you can define each with their own binding, provided you use an annotation during injection

public @interface SomeInterfaceKind {
    MyObjects value();
}

in your Module:

bind(SomeInterface.class)
    .annotatedWith(new SomeInterfaceKindImpl(MyObjects1.OBJECT1)
    .to(Object1.class);
...

Then in classes to be injected:

@Inject void setSomeInterface(
    @SomeInterfaceKind(MyObjects.OBJECT1) SomeInterface object) {...}

Here you have to define SomeInterfaceKindImpl class that implements SomeInterfaceKind (yes, it's possible to extend an annotation!) For more details, take a look at how Named is implemented in Guice.

2. You can also use Guice MapBinder as follows (I find it simpler to implement)

In your Module:

MapBinder.newMapBinder(MyObjects.class, SomeInterface.class)
    .addBinding(MyObjects.OBJECT1).to(Object1.class);
MapBinder.newMapBinder(MyObjects.class, SomeInterface.class)
    .addBinding(MyObjects.OBJECT2).to(Object2.class);

Then in injected methods:

@Inject void setSomeInterface(Map<MyObjects, SomeInterface> map) {
    SomeInterface object1 = map.get(MyObjects.OBJECT1);
    ...
}
like image 199
jfpoilpret Avatar answered Oct 23 '22 04:10

jfpoilpret


You can also use assisted inject

public interface Factory {

    public Object1 createObject1(String param1);
    public Object2 createObject2(Date param2);

}


public class Object1 {
     @AssistedInject
     public Object1(String param1) {
          // do something
     }
}

public class Object2 {
     @AssistedInject
     public Object2(Dateparam2) {
         // do something
     }
}

Then in your module

install(new FactoryModuleBuilder()
     .implement(Object1.class, Object1.class)
     .implement(Object2.class, Object2.class)
     .build(Factory.class));

Then you can use the factory wherever you need it

@Inject
private Factory factory;
like image 26
Thomas Avatar answered Oct 23 '22 06:10

Thomas