Re: Default binding (How to)

554 views
Skip to first unread message

Thomas Broyer

unread,
Sep 6, 2012, 10:00:35 AM9/6/12
to google...@googlegroups.com
This is known as the "robot legs" problem, see http://code.google.com/p/google-guice/wiki/FrequentlyAskedQuestions#How_do_I_build_two_similar_but_slightly_different_trees_of_objec

On Thursday, September 6, 2012 9:14:24 AM UTC+2, robertdup wrote:
Hello there,

I trying to implement default binding on my module without any success...

Here is what I would like to do (my dream) :

class AFoo
{
    @Inject AFoo( IFoo foo ){}
}

class BFoo
{
   @Inject BFoo( IFoo foo ){}
}


bind(IFoo.class).to(DefaultFoo.class);
bind(IFoo.class).to(OtherFoo.class).on(BFoo.class);


I know that I could solve this problem using annotation like this :

class AFoo
{
    @Inject AFoo( @A IFoo foo ){}
}

class BFoo
{
   @Inject BFoo( @B IFoo foo ){}
}


bind(IFoo.class).annotatedWith(A.class).to(DefaultFoo.class);
bind(IFoo.class).annotatedWith(B.class).to(OtherFoo.class);

But this way is too boring and dirty.. (because I have to add annotation/binding definition for each one)

  • Are there some others ways to solve Default binding "problem" ?

Thanks in advance; Best regards

Fred Faber

unread,
Sep 6, 2012, 9:59:09 PM9/6/12
to google...@googlegroups.com
Strictly speaking, the robot legs problem describes a scenario where the types of your object chain are identical.  In your case, you wouldn't have AFoo and BFoo, but just Foo.  It's a luxury of sorts to have AFoo and BFoo because you _can_ use a binding annotation on the constructor of each.  That is the solution I would prefer for its clarity in how AFoo and BFoo are being configured:

class AFoo {
  @Inject AFoo(@DoesStuffRelatedToA IFoo ifoo) { ... }
}

class BFoo {
  @Inject BFoo(@DoesStuffRelatedToB IFoo ifoo) { ... }
}

Here, grepping through the code directly for DoesStuffRelatedToA would lead me to the binding for IFoo in context of AFoo, and that's a little bit of a win for code maintenance.

Fred

On Thu, Sep 6, 2012 at 12:51 PM, robertdup <dupo...@gmail.com> wrote:
Thanks for your reply.

Do you know if it's a common uses to have more than 20 privates modules ?
--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-guice/-/bqRh1CKDwsEJ.

To post to this group, send email to google...@googlegroups.com.
To unsubscribe from this group, send email to google-guice...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-guice?hl=en.

Stephan Classen

unread,
Sep 7, 2012, 8:33:18 AM9/7/12
to google...@googlegroups.com
Maybe you can doe something with either MembersInjector or
InjectionListeners.
See http://code.google.com/p/google-guice/wiki/CustomInjections for details

Stuart McCulloch

unread,
Sep 7, 2012, 8:57:09 AM9/7/12
to google...@googlegroups.com
You could consider writing a provider method for those classes where you want to use the non-default binding.

For example, assuming you use constructor injection:

   public void configure()
   {
      bind( IFoo.class ).to( DefaultFoo.class );
   }

   @Provides
   public E provideE( MyEFoo foo, IBar bar, ...etc... )
   {
      return new E( foo, bar, ...etc... );
   }

   @Provides
   public K provideK( MyKFoo foo, IBar bar, ...etc... )
   {
      return new K( foo, bar, ...etc... );
   }

The downside of this approach is that because you're constructing the instance, it can't be intercepted by Guice (ie. for AOP).

Full example below...

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

import javax.inject.Inject;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Provides;

interface IFoo
{
}

interface IBar
{
}

class DefaultFoo
    implements IFoo
{
}

class DefaultBar
    implements IBar
{
}

class MyBFoo
    implements IFoo
{
}

class A
{
    @Inject
    public A( IFoo foo, IBar bar )
    {
        System.out.println( "Got " + foo + " and " + bar );
    }
}

class B
{
    @Inject
    public B( IFoo foo, IBar bar )
    {
        System.out.println( "Got " + foo + " and " + bar );
    }
}

class C
{
    @Inject
    public C( IFoo foo, IBar bar )
    {
        System.out.println( "Got " + foo + " and " + bar );
    }
}

class ExampleModule
    extends AbstractModule
{
    @Override
    protected void configure()
    {
        bind( IFoo.class ).to( DefaultFoo.class );
        bind( IBar.class ).to( DefaultBar.class );
    }

    @Provides
    B provideB( MyBFoo foo, IBar bar )
    {
        return new B( foo, bar );
    }
}

public class Example
{
    public static void main( String[] args )
    {
        Injector injector = Guice.createInjector( new ExampleModule() );

        System.out.println( "--- A ---" );
        injector.getInstance( A.class );
        System.out.println( "--- B ---" );
        injector.getInstance( B.class );
        System.out.println( "--- C ---" );
        injector.getInstance( C.class );
    }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

On 7 Sep 2012, at 08:02, robertdup wrote:

Yes it's right.. but this way don't satisfy me totally cause I have some XFoo classes.
I will have to create many annotation (@A,@B, ..., @Z), and I must bind all of them or I will get a guice exception.

bind(IFoo.class).annotatedWith.(A.class).to(DefaultFoo.class);
bind(IFoo.class).annotatedWith.(B.class).to(DefaultFoo.class);
[...]
bind(IFoo.class).annotatedWith.(Y.class).to(DefaultFoo.class);
bind(IFoo.class).annotatedWith.(Z.class).to(DefaultFoo.class);

In my case, I just have to override a couple of binding and let the other on the default implementation (DefaultFoo.class)
bind(IFoo.class).annotatedWith.(E.class).to(MyEFoo.class);
bind(IFoo.class).annotatedWith.(K.class).to(MyKFoo.class);

Otherwise, PrivateModule looks too "heavy" to implement in my case..

  • Well, Is there an other way that could be less verbose ?

Best regards;
To view this discussion on the web visit https://groups.google.com/d/msg/google-guice/-/jH07TWjWEZ8J.

Fred Faber

unread,
Sep 7, 2012, 9:57:33 AM9/7/12
to robertdup, google...@googlegroups.com
A pattern to mitigate the boilerplate is to use a parameterized annotation:

  @Retention(RetentionPolicy.RUNTIME)
  @BindingAnnotation
  public @interface DoesFooStuff {
    FooClient value();
    enum WhatKindOfStuff {
      STUFF_THAT_A_WANTS,      
      STUFF_THAT_B_WANTS,
      ...
      STUFF_THAT_Z_WANTS
    }
  }

You would then use this to annotate your Foos:

FooClient class AFoo {
  @Inject AFoo(@DoesStuffForFoo(Foo.WhatKindOfStuff.STUFF_THAT_A_WANTS) IFoo foo) {
    ...
  }
}

In your module you'd define an implementation of the interface (which is tricky...you need to be careful to follow the spec on the Annotation javadoc):

  @SuppressWarnings("ClassExplicitlyAnnotation")
  private static class DoesFooStuffImpl implements DoesStuffForFoo {
private final WhatKindOfStuff value;
    private DoesFooStuffImpl(WhatKindOfStuff value) {
      this.value = value;
    }

    @Override WhatKindOfStuff String value() {
return value; } @Override public Class<? extends Annotation> annotationType() { return DoesStuffForFoo.class;
} @Override public String toString() { return "@" + DoesStuffForFoo.class.getName() + "(value=" + value + ")";
} @Override public boolean equals(Object o) { return o instanceof DoesStuffForFooImpl
&& ((DoesStuffForFoo) o).value().equals(value());
} @Override public int hashCode() { return (127 * "value".hashCode()) ^ value.hashCode(); } }

You could then define a helper method as syntatic sugar over the binding:

private void bindFoo(Class<? extends Foo> fooClass, WhatKindOfStuff whatKindOfStuff {
   bind(Foo.class)
     .annotatedWith(new DoesFooStuffImpl(whatKindOfStuff))
     .to(fooClass); 
}

And then your bindings become:

@Override protected void configure() {
  bindFoo(FooThatAFooWants.class, WhatKindOfStuff.STUFF_THAT_A_WANTS);
  bindFoo(FooThatBFooWants.class, WhatKindOfStuff.STUFF_THAT_B_WANTS);
  ...
  bindFoo(FooThatZFooWants.class, WhatKindOfStuff.STUFF_THAT_Z_WANTS);
 }


Fred

On Fri, Sep 7, 2012 at 3:02 AM, robertdup <dupo...@gmail.com> wrote:
Yes it's right.. but this way don't satisfy me totally cause I have some XFoo classes.
I will have to create many annotation (@A,@B, ..., @Z), and I must bind all of them or I will get a guice exception.

bind(IFoo.class).annotatedWith.(A.class).to(DefaultFoo.class);
bind(IFoo.class).annotatedWith.(B.class).to(DefaultFoo.class);
[...]
bind(IFoo.class).annotatedWith.(Y.class).to(DefaultFoo.class);
bind(IFoo.class).annotatedWith.(Z.class).to(DefaultFoo.class);

In my case, I just have to override a couple of binding and let the other on the default implementation (DefaultFoo.class)
bind(IFoo.class).annotatedWith.(E.class).to(MyEFoo.class);
bind(IFoo.class).annotatedWith.(K.class).to(MyKFoo.class);

Otherwise, PrivateModule looks too "heavy" to implement in my case..

  • Well, Is there an other way that could be less verbose ?

Best regards;


On Friday, September 7, 2012 3:59:12 AM UTC+2, Fred Faber wrote:

Fred Faber

unread,
Sep 7, 2012, 9:59:17 AM9/7/12
to robertdup, google...@googlegroups.com
Noticed the typos now.  I started by naming "DoesFooStuff" naming as "FooClient", so please disregard those references.

On Fri, Sep 7, 2012 at 9:57 AM, Fred Faber <ffa...@faiser.com> wrote:
A pattern to mitigate the boilerplate is to use a parameterized annotation:

  @Retention(RetentionPolicy.RUNTIME)
  @BindingAnnotation
  public @interface DoesFooStuff {
    DoesFooStuff value();
    enum WhatKindOfStuff {
      STUFF_THAT_A_WANTS,      
      STUFF_THAT_B_WANTS,
      ...
      STUFF_THAT_Z_WANTS
    }
  }

You would then use this to annotate your Foos:

Russ Milliken

unread,
Sep 7, 2012, 10:34:30 AM9/7/12
to google...@googlegroups.com, robertdup
I've done the same thing in my project, taking it a step further: I've hidden the annotation implementation in an InvocationHandler and written a simple factory that takes the annotation class and enum value and creates a dynamic proxy for the annotation desired:

public final class AnnotationFactory {

    public static <A extends Annotation,
                   E extends Enum<E>> A create(final Class<A> annoClass,
                                               final E theEnum) {
        // Details omitted for brevity
    }
}


An AnnotationInvocationHandler class (not shown) implements the details of the annotation specification.  You wouldn't then be forced to re-implement the annotation implementation for each new annotation class you create, just create them programatically using the factory.

Your code could then look like this:

private void bindFoo(Class<? extends Foo> fooClass,
                     WhatKindOfStuff whatKindOfStuff) {
   bind(Foo.class)
     .annotatedWith(AnnotationFactory.create(DoesFooStuff.class,
                                             whatKindOfStuff))
     .to(fooClass); 
}

This makes DoesFooStuffImpl class unnecessary.

-Russ
--
Gambling Problem? Call 1-800-Gambler

robertdup

unread,
Sep 7, 2012, 10:44:32 AM9/7/12
to google...@googlegroups.com
@scl
I've still not practices with Custom Injection. I will take a look !

@Stuart McCulloch
This way looks good;even if it's a pity to lose Guice AOP =)

@Fred Faber & @Russ
I think that I will use this solution...

Thanks you for all your proposition; I appreciate !
Someday, perhaps, Guice will implement a functionnality to do it more easily =)

Christian Gruber

unread,
Sep 7, 2012, 1:36:28 PM9/7/12
to google...@googlegroups.com
There are different possibilities under discussion.  The main problem is that we are moving more towards up-front validation and static analysis, so the more that can be swapped-in over a default, the trickier some  of that gets.  
To view this discussion on the web visit https://groups.google.com/d/msg/google-guice/-/1JhF9-nY1McJ.

glenviewjeff

unread,
Sep 7, 2012, 8:53:00 PM9/7/12
to google...@googlegroups.com
Not to hijack this thread, but will this mean increased start-up performance?

Christian Gruber

unread,
Sep 8, 2012, 12:30:23 AM9/8/12
to google...@googlegroups.com
No - we're talking about build-time up-front validation.
To view this discussion on the web visit https://groups.google.com/d/msg/google-guice/-/DMiQb3afXlYJ.
Reply all
Reply to author
Forward
0 new messages