GoF standard factory pattern using Guice
Asked Answered
C

1

6

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.

Chilopod answered 10/1, 2015 at 0:47 Comment(4)
I'm not sure what exactly you are trying to do but isn't it somewhat against the idea of dependency injection if the code that needs an instance tells the factory (or injector, for that matter) what implementation it wants?Divisionism
In my case, I am writing certain action handler classes. I allow certain specific actions to be performed, but I need to get an instance of the right action handler based on the User action that was performed.Chilopod
After posting my answer below, I did some searching and found the same question here with a much better answer.Cosmic
related post: medium.com/@jamesonwilliams/…Outofdoors
C
3

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?

Cosmic answered 11/1, 2015 at 3:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.