Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GoF standard factory pattern using Guice

I have used the standard factory pattern method before to create instances of classes (implementing a specific interface) using a Factory class, which has a "create" method, that returns the right instance based on the parameter passed to it (example snippet given below):

public class SimpleFactory {
    public static SimpleObjectInterface getSimpleObject(int data) {
         SimpleObjectInterface toReturn;
          switch(data) {
           case 1:
            toReturn = new OneSimpleObject();
           break;
          case 2:
            toReturn = new TwoSimpleObject();
          break;
          default:
             toReturn = new DefaultSimpleObject();    
          break;  
        }
        return toReturn;
      }
}

Now I am using Guice in my project for dependency injection. My question is how can I achieve something like the above using Guice? Which implementation instance is needed is decided at runtime based on some user input.

I have looked at Provider and @Named annotations. But I don't understand how exactly it will help me.

like image 734
user1790625 Avatar asked Jan 10 '15 00:01

user1790625


People also ask

When would you use the GOF Factory Method design pattern?

Factory method lets a class defer instantiation to subclass. Factory needs an object (whose concrete class is not known or whose concrete class may change as per the different application type ) to perform a task.

Does Google use Guice?

Guice is an open source, Java-based dependency injection framework. It is quiet lightweight and is actively developed/managed by Google.

What does Guice Createinjector do?

Using GuiceIf it's a simple object, it'll instantiate it and pass it in. If it has dependencies, it will resolve those dependencies, pass them into it's constructor, then pass the resulting object into your object.


1 Answers

In general for the problem where you want a factory that injects most dependencies, but still allows some client-supplied deps, you would use Factories by Assisted Injection.

However in your case this would lead to conditional logic in your factory, which is probably not ideal (it is explicitly discouraged in Guice modules).

I think for your situation a MapBinder would be ideal, and you wouldn't need a factory at all, since you're only switching on data type and not building anything really. In your module you configure a map of int (in your case) keys to impls of SimpleObjectInterface. Then in your main runtime class you inject the map, and when you need an instance of a simple object and have int data available, you call get(data) on the injected map.

I don't have an IDE on this machine, so I can't test the code, but from memory it would be something like below:

In your module:

public class MyModule extends AbstractModule {
  protected void configure() {
    MapBinder<Integer, SimpleObjectInterface> mapbinder
        = MapBinder.newMapBinder(binder(), Integer.class, SimpleObjectInterface.class);
    mapbinder.addBinding(1).toClass(OneSimpleObject.class);
    mapbinder.addBinding(2).toClass(TwoSimpleObject.class);
  }
}

In your app code:

@Inject
private Map<Integer, SimpleObjectInterface> simpleObjectMap;

...

void applicationCode() {
  ...
  Integer data = getData();
  SimpleObjectInterface simpleObject = simpleObjectMap.get(data);
  ...
}

Only issue here is you can't have the "default" binding that you had in your switch statement. Not sure of the best way to handle that, maybe you could assign a default impl in your app code if the object is still null after trying to instantiate it from the map binder. Or you could go back to assisted inject with conditional logic, but it's not really "assisted" injection if the sole dependency is client supplied.

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

like image 137
The111 Avatar answered Sep 23 '22 20:09

The111