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
My question for today is: What is the correct way to bind a single
implementation to multiple interfaces?
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?
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
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);
Is the Provider approach intended to help with the lazy instantiation only?
bind(SimpleCallback.class).to(MassiveCallbackImpl.class).in(Scopes.Singleton);
bind(AnotherCallback.class).to(MassiveCallbackImpl.class).in(Scopes.Singleton);
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
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.
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.