My favorites | Sign in
Google
                
Search
for
Updated Jun 08, 2009 by limpbizkit
AssistedInject  
an easier way to help the Guice Injector build objects

AssistedInject

Factories by Hand

Sometimes a class gets some of its constructor parameters from the Guice Injector and others from the caller:

public class RealPayment implements Payment {
  public RealPayment(
        CreditService creditService,  // from the Injector
        AuthService authService,  // from the Injector
        Date startDate, // from the instance's creator
        Money amount); // from the instance's creator
  }
  ...
}

The standard solution to this problem is to write a factory that helps Guice build the objects:

public interface PaymentFactory {
  public Payment create(Date startDate, Money amount);
}
public class RealPaymentFactory implements PaymentFactory {
  private final Provider<CreditService> creditServiceProvider;
  private final Provider<AuthService> authServiceProvider;

  @Inject
  public PaymentFactory(Provider<CreditService> creditServiceProvider,
      Provider<AuthService> authServiceProvider) {
    this.creditServiceProvider = creditServiceProvider;
    this.authServiceProvider = authServiceProvider;
  }

  public Payment create(Date startDate, Money amount) {
    return new RealPayment(creditServiceProvider.get(),
      authServiceProvider.get(), startDate, amount);
  }
}

...and a corresponding binding in the module:

   bind(PaymentFactory.class).to(RealPaymentFactory.class);

It's annoying to write the boilerplate factory class each time this situation arrises. It's also annoying to update the factories when the implementation class' dependencies change.

Factories by AssistedInject

AssistedInject generates an implementation of the factory class automatically. To use it, annotate the implementation class' constructor and the fields that aren't known by the injector:

public class RealPayment implements Payment {
  @Inject
  public RealPayment(
        CreditService creditService,
        AuthService authService,
        @Assisted Date startDate,
        @Assisted Money amount);
  }
  ...
}

Then bind a Provider<Factory> in the Guice module:

bind(PaymentFactory.class).toProvider(
    FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));

The FactoryProvider maps the create() method's parameters to the corresponding @Assisted parameters in the implementation class' constructor. For the other constructor arguments, it asks the regular Injector to provide values.

With FactoryProvider, it's easier to create classes that need extra arguments at construction time:

  1. Annotate the constructor and assisted parameters on the implementation class (such as RealPayment)
  2. Create a factory interface with a create() method that takes only the assisted parameters. Make sure they're in the same order as in the constructor
  3. Bind that factory to a provider created by FactoryProvider.


Comment by limpbizkit, Mar 30, 2009

Feature wishlist:

  • build several implementation types with one factory
  • support several constructors within one concrete type
  • allow the factory return type to be bound independently, useful as a default value

Comment by Leigh.Klotz, Jul 31, 2009

It would nice to have something on "Making parameter types distinct" here.

http://google-guice.googlecode.com/svn/trunk/latest-javadoc/com/google/inject/assistedinject/FactoryProvider.html

Making parameter types distinct The types of the factory method's parameters must be distinct. To use multiple parameters of the same type, use a named @Assisted annotation to disambiguate the parameters. The names must be applied to the factory method's parameters:

public interface PaymentFactory? {

Payment create(
@Assisted("startDate") Date startDate, @Assisted("dueDate") Date dueDate, Money amount);
}

...and to the concrete type's constructor parameters:

public class RealPayment? implements Payment {

@Inject public RealPayment?(
CreditService? creditService, AuthService? authService, @Assisted("startDate") Date startDate, @Assisted("dueDate") Date dueDate, @Assisted Money amount) {
...
}
}


Sign in to add a comment