I would like some suggestions and feedback on the best way to structure dependency injection for a system with the structure described below. I'm using Guice and thus would prefer solutions centered around it's annotation-based declarations, not XML-heavy Spring-style configuration.
Consider a set of similar objects, Ball, Box, and Tube
, each dependent on a Logger
, supplied via the constructor. (This might not be important, but all four classes happen to be singletons --- of the application, not Gang-of-Four, variety.)
A ToyChest
class is responsible for creating and managing the three shape objects. ToyChest
itself is not dependent on Logger
, aside from creating the shape objects which are.
The ToyChest
class is instantiated as an application singleton in a Main
class.
I'm confused about the best way to construct the shapes in ToyChest
. I either (1) need access to a Guice Injector
instance already attached to a Module
binding Logger
to an implementation or (2) need to create a new Injector
attached to the right Module
.
(1) is accomplished by adding an @Inject Injector injector
field to ToyChest
, but this feels weird because ToyChest
doesn't actually have any direct dependencies --- only those of the children it instantiates.
For (2), I'm not sure how to pass in the appropriate Module
.
Am I on the right track? Is there a better way to structure this?
The answers to this question mention passing in a Provider
instead of using the Injector directly, but I'm not sure how that is supposed to work.
EDIT:
Perhaps a more simple question is: when using Guice, where is the proper place to construct the shapes objects? ToyChest
will do some configuration with them, but I suppose they could be constructed elsewhere. ToyChest
(as the container managing them), and not Main
, just seems to me like the appropriate place to construct them.
A proper way is to have guice construct your dependencies. That is create and configure.
In your situation you should have an injector constructed in the
Main
. From the injector you getToyChest
. When you obtainToyChest
through the injector its managed by guice and you can depend on it to supply all dependencies properly configured.In your case you can inject
Provider<Ball>
,Provider<Box>
, etc. inToyChest
and when needed just retrieve instance from the provider.ToyChest
is not responsible for constructing the instance, just to use it. You can also check MapBinder if you have a plugin architecture.So far everything is managed by guice, so the shapes can have their logger injected without the using class knowing about it.
If you have some runtime parameters that you want to pass to the newly created shape instances, you can use AssistedInject.
Just a hint: you are not required to use constructor injection, you can have injection on field or setter, which simplifies the constructor.