Juan
Juan

Reputation: 126

Guice AssistedInjection Factory with logic

I've been using guice for a project.

I have an abstract class which has many implementations. To use the right implementation I use a factory that receives a parameter and then returns the right instance.

Demo Code

@Singleton
public class MyFactory {

    private final Foo foo;

    @Inject
    public MyFactory(final Foo foo) {
        this.foo = foo;
    }

    public A create(final Bar bar) {
        switch (bar) {
            case 0:
                return new B(foo, bar);
            case 1:
                return new C(foo, bar);
            //this goes on
        }
    }
}

public abstract A {
    public A(final Bar bar) {
        //do sth
    }
}

public B extends A {
    private final Foo foo;

    public B(final Foo foo, final Bar bar) {
        super(bar);
        this.foo = foo;
    }
}

public C extends A {
    private final Foo foo;

    public C(final Foo foo, final Bar bar) {
        super(bar);
        this.foo = foo;
    }
}

What I want to know, if I can replace the factory with Guice to inject directly the implementations of A (note that they should use assisted injection)?

Thanks.

Upvotes: 1

Views: 726

Answers (1)

Jeff Bowman
Jeff Bowman

Reputation: 95614

You will still need MyFactory to choose an implementation based on your id, though your assisted injection can be very short.

// No need for this to be @Singleton;
// if you want the same shared Foo instance, make it @Singleton
public class MyFactory {

    private final B.Factory bFactory;
    private final C.Factory cFactory;

    @Inject
    public MyFactory(B.Factory bFactory, C.Factory cFactory) {
        this.bFactory = bFactory;
        this.cFactory = cFactory;
    }

    public A create(final Bar bar) {
        switch (bar.getSomeInteger()) {   // I assume you're checking a
                                          // property of bar
            case 0:
                return bFactory.create(bar);
            case 1:
                return cFactory.create(bar);
            //this goes on
        }
    }
}

public B extends A {
    public interface Factory {
        B create(Bar bar);
    }

    private final Foo foo;

    public B(final Foo foo, @Assisted final Bar bar) {
        super(bar);
        this.foo = foo;
    }
}

public C extends A {
    public interface Factory {
        C create(Bar bar);
    }

    private final Foo foo;

    public C(final Foo foo, @Assisted final Bar bar) {
        super(bar);
        this.foo = foo;
    }
}

And your Module:

public class YourModule extends AbstractModule {
  @Override public void configure() {
    install(new FactoryModuleBuilder().build(B.Factory.class));
    install(new FactoryModuleBuilder().build(C.Factory.class));
  }
}

Edit: You do not need a call to implement on FactoryModuleBuilder in my example, because B.Factory has a create method that returns your subclass B. If you wanted the method to return your superclass A, hiding the concrete type, you could do that; then you would need the implement call, because Guice wouldn't know which constructor to try to call.

If you wanted to force consumers to code to the implementation, you might want to refer to a factory that only returns the interface. That would generally be a good idea to hide the implementation details, and would probably involve creating A.Factory with method A create(Bar bar) and wiring it up with implement. However, that is unnecessary here because your MyFactory already returns A and hides the implementing subclass (acting like A.Factory with logic), and because you would need @Named or some other qualifier annotation to distinguish between the two A.Factory bindings you're creating. In short, that's extra complication with no benefit for this specific case.

Upvotes: 1

Related Questions