19

I have three classes CircleBuilder, SquareBuilder, and TriangleBuilder implementing the ShapeBuilder interface.

I need to initialize my FormBuilder and notably a BuilderList (extending List<ShapeBuilder>) with one instance of each class with google-guice.

What is the best way?

I know about the provider methods and stuff like this:

@Provides
FormBuilder provideFormBuilder() {
    DatabaseTransactionLog instance = new FormBuilder ( <numerous parameters> );

    ShapeBuilder builder = null ; 
    builder = new CircleBuilder( <numerous parameters> ) ;  
    instance.addBuilder( builder ) ;

    builder = new SquareBuilder( <numerous parameters> ) ;  
    instance.addBuilder( builder ) ;

    // And so on

    return instance;
}

but it would mean that I have to create my FormBuilder manually which defeats the purpose of using guice (because FormBuilder is the top element in my object graph).

I'd love to be able to write something like this:

bind(BuilderList.class).to(CircleBuilder.class);
bind(BuilderList.class).to(TriangleBuilder.class);
bind(BuilderList.class).to(SquareBuilder.class);

Any idea?

Chris Mantle
  • 6,595
  • 3
  • 34
  • 48
Name is carl
  • 5,961
  • 3
  • 29
  • 44

2 Answers2

23

Consider Multibindings, which will collect bindings very much like your code snippet. There is no provision for lists through Multibinder, because Multibinder is designed for binding to the same collection in multiple modules, and the element order of a Multibinder list would depend on the order that your Modules were evaluated.

Multibinder<ShapeBuilder> shapeBinder =
    Multibinder.newSetBinder(binder(), ShapeBuilder.class);
shapeBinder.addBinding().to(CircleBuilder.class);
shapeBinder.addBinding().to(TriangleBuilder.class);
shapeBinder.addBinding().to(SquareBuilder.class);

// Now you can inject Set<ShapeBuilder>.

Alternatively, your @Provides method can take in parameters (e.g. CircleBuilder or Provider<CircleBuilder>) so you can have Guice create everything except the List itself. Not only will Guice bind Providers of all bound types automatically, but it will also inject every parameter in any @Provides method.

@Provides List<ShapeBuilder> provideShapeBuilders(
    CircleBuilder circleBuilder,
    SquareBuilder squareBuilder,
    TriangleBuilder triangleBuilder,
    Provider<TrapezoidBuilder> trapezoidBuilderProvider) {
  return new ArrayList<ShapeBuilder>(
      circleBuilder,
      squareBuilder,
      triangleBuilder,
      trapezoidBuilderProvider.get(),
      trapezoidBuilderProvider.get());
}
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • 3
    Note : multibinding requires another jar -> guice-multibindings-3.0.jar – Name is carl Jan 07 '13 at 22:54
  • My question (sof url i'm including here) shows how to use Mulitbinder and then constructor-inject the "set" of concretes into the class that needs them (for those inclined to constructor based injection). https://stackoverflow.com/questions/52279459/guice-with-multiple-concretes-picking-one-of-them – granadaCoder Sep 11 '18 at 19:35
  • This is the current link to Multibindings user guide: https://github.com/google/guice/wiki/Multibindings – Uziel Sulkies Jan 30 '19 at 15:08
2

We can do something like this:-

@Override
protected void configure() {
    bind(ShapeBuilder.class).annotatedWith(Names.named("Circle")).to(CircleBuilder.class);
    bind(ShapeBuilder.class).annotatedWith(Names.named("Triangle")).to(TriangleBuilder.class);
    bind(ShapeBuilder.class).annotatedWith(Names.named("Square")).to(SquareBuilder.class);
}

In your FormBuilder Class:-

class FormBuilder{
   List<ShapeBuilder> shapeBuilderList;
   @Inject
   public FormBuilder(@Named("Circle")ShapeBuilder circle, @Named("Triangle")ShapeBuilder triangle,@Named("Square")ShapeBuilder square){
       shapeBuilderList = new ArrayList<>();
       //add all of them
       shapeBuilderList.add(circle);
    }
}
Kartik Goyal
  • 459
  • 3
  • 15