Thomas
Thomas

Reputation: 1123

How to use Guice Dependency Injection to get object in class that doesn't have main method?

I have a class that does not have the main method. This class is also used by code that is not in my control. This class has a method that takes a String as a parameter, and then based on the value of that String it needs to create different objects. How can I use Guice to create these objects? I think the answer has something to do with Providers, but I'm not sure how to implement it.

Here is the class in question, which doesn't use DI to make the objects:

public class ActionHandler {

    public void doSomething(String message) throws Exception {

        ActionObject actionObj = null;

        if (message.equals("dog")) {
            // ActionObjectDog implements ActionObject
            actionObj = new ActionObjectDog(); 
        }
        else if (message.equals("cat")) {
            // ActionObjectCat implements ActionObject
            actionObj = new ActionObjectCat(); 
        }
        else {
            throw new Exception("Action " + message + " not implemented.");
        }

        actionObj.run();
    }
}

I was trying to inject a Provider, but I got stuck because I couldn't figure out how to make the Provider get() method return a specific implementation of ActionObject.

@Inject private Provider<ActionObject> actionObjectProvider;

Any thoughts?

Upvotes: 3

Views: 705

Answers (1)

smjZPkYjps
smjZPkYjps

Reputation: 1178

A Provider won't work for your situation because, as you have discovered, get() doesn't take any parameters.

One option is to use a MapBinder:

class Module extends AbstractModule {
  @Override protected void configure() {
    MapBinder.newMapBinder(binder(), String.class, ActionObject.class)
        .addBinding("dog").to(ActionObjectDog.class);
    MapBinder.newMapBinder(binder(), String.class, ActionObject.class)
        .addBinding("cat").to(ActionObjectCat.class);
  }
}

class ActionHandler {
  private final Map<String, ActionObject> mappings;
  @Inject ActionHandler(Map<String, ActionObject> mappings) {
    this.mappings = mappings;
  }

  public void doSomething(String message) {
    Preconditions.checkNotNull(mappings.get(message), "No action for '" + message + "'")
        .run();
  }
}

It's not leveraging any awesome Guiciness, but it moves the ActionObject selection logic out of ActionHandler, which is what it sounds like you're trying to do. The injected map is just a Collections.UnmodifiableMap, so the same instance will be returned with each call to mappings.get(...), which means your ActionObject implementations would need to be threadsafe.

Also, I'm confused by the specific attention being paid to the lack of a main method. Perhaps I'm misunderstanding the problem, but you can create a Guice Injector from anywhere you'd like. If the above solution doesn't work for you, can you expand on that restriction?

Upvotes: 2

Related Questions