As Sam mentioned. It is absolutely possible (and my prefered way) to
inject all dependencies into the constructor. This way the class can
easily be created without DI in a unit test. And take a look at assisted
injection.
On 11/11/2013 04:35 PM, Sam Berlin wrote:
Hi Alan, some thoughts inline below.
On Mon, Nov 11, 2013 at 10:03 AM, Alan Darkworld
<[email protected] <mailto:[email protected]>> wrote:
Hello everyone,
I've been working with Java for several years now and I'm always
open for new ideas to improve code quality. But I do have some
serious doubts about dependency injection (more on that later).
However, as large frameworks such as Eclipse 4 and Xtext employ it
successfully, I'm trying to get used to this new idiom. When
walking through some tutorials, several questions appeared:
*1)* The Injector becomes a sort of "main static factory" for the
entire application. However, Guice does not instance-control the
injector - in other words, Injector#getInstance is not static. How
do you share your injector across all classes in your application
such that it's easy to access? The easiest thing I can think of is
to create it once at the start of the program and then make it
available through a static field, but that just doesn't feel
"right" to me.
This isn't really the right angle. This line of thought is more akin
to non-DI programming -- where you have a single static "service
locator", and other pieces of code call out to that locator and ask
for objects. That isn't what DI is about. DI is about ensuring that
your objects only care about exactly what they need and nothing more.
No dependencies on this big "whale" objects. You construct your
injector and get the "entry point" of your application (e.g, something
like MyApp.class), and MyApp has its dependencies injected, its
dependencies have theirs injected, etc... recursively all the way
down. No class ever needs to look back at the Injector, because the
Injector has already figured out each specific dependency and knows
how to create the class properly.
(There are, of course, some advanced use-cases where you may need the
Injector... but those are typically for extension-like circumstances,
and not in common usages. In those cases, you simply list the
Injector as another dependency and it will inject itself. No statics
required.)
*2)* I can see that DI is handy if all objects have no-argument
default constructors. But most of the time, this is not the case,
especially with immutable objects that need to check their
invariants in their constructor. Consider for example an immutable
"DateRange" class with the invariant "start date < end date". The
only valid constructor for this class has two parameters (the
dates), yielding a client code like:
long start = calculateStart(); // calculates the start time based
on user input (result is dynamic)
long end = calculateEnd(); // calculates end time based on user
input (result is dynamic)
DateRange period = new DateRange(start, end); // throws
IllegalArgumentException if start > end), need to replace this
line by Depencency Injection
How would something like this translate into the Dependency
Injection idiom? I can clearly see DI working for the Java Beans
pattern, but what about the immutable case? I've had a look at the
Guice Documentation, but I couldn't spot an answer to this.
Simple bean-like classes like DateRange aren't things that should be
injected. Those are just simple data classes. There's no need to get
fancy and do anything with dependency injection there. DI is more
useful when there's actual dependencies -- "start"/"end" and other
user-supplied parameters aren't things that are injected. If the
entire class is made up of user-supplied values, then it isn't
something DI should handle.
I'm not sure where you found that DI is useful for no-args constructs.
DI isn't very useful there. DI is useful when you have constructors
that take system-supplied arguments (e.g, ExperimentManager depends on
ActiveExperiments and RpcSystem).
(And again, there are cases where you may have an object that depends
on some mix of user-supplied & system-supplied values. For those
cases, there's an extension called AssistedInject
<https://code.google.com/p/google-guice/wiki/AssistedInject>... but
that's more advanced usage.)
*3)* For many years, several books (including the excellent
"/Effective Java/") have gone to great lenghts explaining how to
keep our class invariants safe from both malicious access and
careless programmers (including client use and subclassing). By
applying DI, we forfeit the control over our fields, we give up on
encapsulation. From my point of view, with DI the focus has
shifted from "/This class will work, no matter how much you
mistreat it/" to "/It works if you treat it right, otherwise it's
your own fault/". What implications does that have for software
quality? We sure gain a lot in terms of configurability (create a
new subclass, introduce it in the module and you're all set), but
what do we lose with respect to safety and contracts? This is
actually my main concern about DI and the very reason why I can't
yet wrap my head around it. Can somebody provide a reason why my
concerns are without cause? Please do!
I'm not sure what leads you to that conclusion. You can still easily
check invariants with DI. In fact, it's easier to check with DI
because, by injecting your dependencies, your class is stating
upfront: I care about <this>, and <that>. With a service locator
pattern, it instead either says nothing or says: I care about
<something that may be inside that that>. So your tests don't know
exactly how to construct it, and casual readers of the code have to
analyze the entire class for usage instead of just looking at the
constructor.
I do have some more concerns about DI, but the three stated above
are the ones which really prevent me from getting into it. I'd be
grateful if someone could explain how to do it the right way (for
1 and 2), or why I'm wrong (for 3).
Have you watched the video
<https://www.youtube.com/watch?v=hBVJbzAagfs>on the Guice front page?
Bob & Dhanji do a very good job of explaining how it works.
sam
Thanks,
Alan
--
You received this message because you are subscribed to the Google
Groups "google-guice" group.
To unsubscribe from this group and stop receiving emails from it,
send an email to [email protected]
<mailto:google-guice%[email protected]>.
To post to this group, send email to [email protected]
<mailto:[email protected]>.
Visit this group at http://groups.google.com/group/google-guice.
For more options, visit https://groups.google.com/groups/opt_out.
--
You received this message because you are subscribed to the Google
Groups "google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/google-guice.
For more options, visit https://groups.google.com/groups/opt_out.
--
You received this message because you are subscribed to the Google Groups
"google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/google-guice.
For more options, visit https://groups.google.com/groups/opt_out.