How to bind one implementation to multiple interfaces?

2,386 views
Skip to first unread message

Sam Berlin

unread,
Jun 28, 2007, 5:37:59 PM6/28/07
to google-guice
Hi Folks,

We're attempting to use Guice to help us cleanup our dependencies and
various static initializers, etc... I predict I'll be sending many
questions over the next few weeks and months, as our code is now using
the service locator pattern, with the lookups either being static
methods in one ReallyComplexAndHugeContainer class or direct
MyPoorlyExposedService.instance() calls, so there's a lot of cleaning
up to do.

My question for today is: What is the correct way to bind a single
implementation to multiple interfaces?

For example, consider the following interfaces:

interface SimpleCallback;
interface AnotherCallback;
interface YetAnotherCallback;
interface MassiveCallback extends SimpleCallback, AnotherCallback,
YetAnotherCallback;
class MassiveImpl implements MassiveCallback;

Some classes are designed to take a SimpleCallback, others
AnotherCallback, others YetAnotherCallback, and others
MassiveCallback. In all cases, it's the same singleton instance
that's being passed to these services.

Is there a way of binding these interfaces to an implementation
without providing the instance of the implementation during the
binding? That is, the goal would be to bind the interfaces to an
instance that is lazily constructed. Do I need to use a
Provider<MassiveCallback> that constructs upon the first 'get' and
returns the same one? If that's the way to do it, how do I inject the
right MassiveCallback implementation for the Provider to use?

Thanks!
Sam

Dhanji R. Prasanna

unread,
Jun 28, 2007, 7:09:23 PM6/28/07
to google...@googlegroups.com
On 6/29/07, Sam Berlin <sbe...@gmail.com> wrote:

My question for today is:  What is the correct way to bind a single
implementation to multiple interfaces?

You need to bind them individually:

bind(SimpleCallback.class).to(MassiveCallback.class );
bind(AnotherCallback.class).to(MassiveCallback.class);
//...

The idea is that you dont care what the impl is (tomorrow your SimpleCallback could hook to MyImpl.class); you just care about the contract of usage (interface) and let guice do the wiring.

Is there a way of binding these interfaces to an implementation
without providing the instance of the implementation during the
binding?  That is, the goal would be to bind the interfaces to an
instance that is lazily constructed.  Do I need to use a
Provider<MassiveCallback> that constructs upon the first 'get' and
returns the same one?  If that's the way to do it, how do I inject the
right MassiveCallback implementation for the Provider to use?

You would create a provider for each interface and bind MassiveCallback as a singleton:

bind(MassiveCallback.class).in(Singleton.class);
bind(SimpleCallback.class).toProvider(SimpleCallbackProvider.class);   //will result in instantiation of m when first called

public class SimpleCallbackProvider implements Provider<SimpleCallback> {
    @Inject MassiveCallback m;

    public SimpleCallback get() { return m; }
}

//repeat for each interface...

Unfortunately, without violating the type system I dont see any way of using just the one provider to provide for all interfaces.

Dhanji.

Sam Berlin

unread,
Jun 29, 2007, 1:02:28 PM6/29/07
to google...@googlegroups.com
Thanks for your answer, Dhanji.

To be clear, do I need to use the Provider approach if I want a single
instance of the implementation? Would...

bind(SimpleCallback.class).to(MassiveCallbackImpl.class).in(Scopes.Singleton);
bind(AnotherCallback.class).to(MassiveCallbackImpl.class).in(Scopes.Singleton);

... create just the one singleton instance of MassiveCallbackImpl to
bind to both (and not one singleton instance per type it was bound
to)? I could see arguments for doing it either way, but don't think I
saw any definitive answer.

Is the Provider approach intended to help with the lazy instantiation only?

Thanks,
Sam

Dhanji R. Prasanna

unread,
Jun 29, 2007, 6:23:13 PM6/29/07
to google...@googlegroups.com
On 6/30/07, Sam Berlin <sbe...@gmail.com> wrote:

Thanks for your answer, Dhanji.

To be clear, do I need to use the Provider approach if I want a single
instance of the implementation?  Would...

bind(SimpleCallback.class).to(MassiveCallbackImpl.class ).in(Scopes.Singleton);
bind(AnotherCallback.class).to(MassiveCallbackImpl.class).in(Scopes.Singleton);

This would create 2 instances of MassiveCallbackImpl. The scopes are effective per-binding rather than per class impl.

The providers (for each) let you converge the two bindings on the same instance of the impl by using a level of indirection (where you inject the providers with a shared instance and provide that to each binding).

Is the Provider approach intended to help with the lazy instantiation only?

Not as such. Every binding example so far (in this thread) will be lazily instantiated. Eager instantiation is only in the following cases:

bind(..).toInstance(...);  //eager
bind(..).asEagerSingleton(); //eager

bind(..).toProvider(new Provider()) //provider is eagerly instantiated BUT instance is not

The provider is meant to cater for situations where you need to do some additional setup work or where you're using sealed code (that cant take @Inject annotations) that needs injection (for example, injecting the Hibernate SessionFactory).

Dhanji.

Bob Lee

unread,
Jun 29, 2007, 6:31:46 PM6/29/07
to google...@googlegroups.com
On 6/29/07, Sam Berlin <sbe...@gmail.com> wrote:
bind(SimpleCallback.class).to(MassiveCallbackImpl.class).in(Scopes.Singleton);
bind(AnotherCallback.class).to(MassiveCallbackImpl.class).in(Scopes.Singleton);

Sam, if you wanted just one instance, you could do this:

  bind(MassiveCallbackImpl.class).in(Scopes.SINGLETON);
  bind(SimpleCallback.class).to(MassiveCallbackImpl.class );
  bind(AnotherCallback.class).to(MassiveCallbackImpl.class);

You could also drop that first line and put @Singleton on MassiveCallbackImpl.

Bob

Sam Berlin

unread,
Jun 29, 2007, 7:41:09 PM6/29/07
to google...@googlegroups.com
Ah, that seems easy enough.

So if ever a singleton implementation is being used as the binding for
multiple interfaces, that implementation needs to be bound separately
as a singleton, and the interfaces need to be wired to that
implementation class without specifying the scope themselves. Makes
sense when you wrap your mind around the idea that the Scopes are
specific to what's being bound from, not what's being bound to.

As far as adding @Singleton in MassiveCallbackImpl -- is this
something you recommend when using adopting Guice? Is the ease-of-use
in writing modules worth it to use the convenience @ImplementedBy and
@Singleton annotations? Or does this lead to bad code coupling or
other bad practices?

Would Guice support something like:

bind(SimpleCallback.class).to(MassiveCallback.class);
bind(AnotherCallback.class).to(MassiveCallback.class);
bind(MassiveCallback.class).to(MassiveCallbackImpl.class);
bind(MassiveCallbackImpl.class).in(Scopes.SINGLETON);

(Where MassiveCallback is an interface that extends SimpleCallback &
AnotherCallback). The idea behind that would be that you are ceding
control of the implementation that SimpleCallback & AnotherCallback
are using to whatever MassiveCallback is bound to. Kind of a
hierarchy of binding.

Thanks,
Sam

Bob Lee

unread,
Jun 29, 2007, 7:50:24 PM6/29/07
to google...@googlegroups.com
On 6/29/07, Sam Berlin <sbe...@gmail.com> wrote:
As far as adding @Singleton in MassiveCallbackImpl -- is this
something you recommend when using adopting Guice?  Is the ease-of-use
in writing modules worth it to use the convenience @ImplementedBy and
@Singleton annotations?  Or does this lead to bad code coupling or
other bad practices?

I always use @Singleton when possible. It makes it clear to someone who is editing the class that it's a singleton and they need to think about concurrency, etc. I only use in() when I can't use a scope annotation.

I personally never use @ImplementedBy. It's convenient but not my cup of tea.


Would Guice support something like:

bind(SimpleCallback.class).to(MassiveCallback.class);
bind(AnotherCallback.class).to(MassiveCallback.class);
bind(MassiveCallback.class).to(MassiveCallbackImpl.class);
bind(MassiveCallbackImpl.class).in(Scopes.SINGLETON);

(Where MassiveCallback is an interface that extends SimpleCallback &
AnotherCallback).  The idea behind that would be that you are ceding
control of the implementation that SimpleCallback & AnotherCallback
are using to whatever MassiveCallback is bound to.  Kind of a
hierarchy of binding.

Yes, that would work. bind() really just creates links from binding to binding.

Bob


Dhanji R. Prasanna

unread,
Jun 29, 2007, 8:31:34 PM6/29/07
to google...@googlegroups.com
On 6/30/07, Bob Lee <craz...@crazybob.org> wrote:

Sam, if you wanted just one instance, you could do this:

  bind(MassiveCallbackImpl.class).in(Scopes.SINGLETON);
  bind(SimpleCallback.class).to(MassiveCallbackImpl.class );
  bind(AnotherCallback.class).to(MassiveCallbackImpl.class);

You could also drop that first line and put @Singleton on MassiveCallbackImpl.

...or that was the simple answer =) hehe

Dhanji.
Reply all
Reply to author
Forward
0 new messages