Dependency Injection has become a much more accepted and accessible approach in recent years, driven by many factors including increased popularity in SOA, TDD, and many other factors. With this has come increased usage of Dependency Injection frameworks, highlighted by its recent inclusion in Java EE 6. Bob Martin advises, with examples, applying a decoupling approach between your application code and your Dependency Injection framework of choice.
In his Dependency Injection Inversion article, [Uncle Bob] Martin presents a message that boils down to this simple statement, in his words:
... I don’t want [Dependency Injection] framework code smeared all through my application. I want to keep frameworks nicely decoupled and at arms-length from the main body of my code.
To illustrate this point, Martin sets up an example revolving around the creation of BillingService class having the following dependencies defined by its constructor:
public class BillingService { ... BillingService(CreditCardProcessor processor, TransactionLog transactionLog) { this.processor = processor; this.transactionLog = transactionLog; } ... }
He first introduces an snippet that uses Guice (Google's Dependency Injection framework) to create an instance of the BillingService class:
public static void main(String[] args) { Injector injector = Guice.createInjector(new BillingModule()); BillingService billingService = injector.getInstance(BillingService.class); billingService.processCharge(2034, "Bob"); }
After going through some of the details behind what's really happening here (with Guice), Martin picks on the fact that you're now in the position of having to explicitly ask Guice for an injector to create instances of the BillingService. So, code that needs a BillingService no longer has a hard dependency on BillingService's dependencies (A Good Thing), but it now has a hard dependency on Guice.
Have you just traded one evil for another? Martin says yes:
Dependency Injection is just a special case of Dependency Inversion. I think Dependency Inversion is so important that I want to invert the dependencies on Guice! I don’t want lots of concrete Guice dependencies scattered through my code.
Later, he shows how a hand-rolled Factory-like object might be used to control and reduce the application's dependency on the D.I framework:
public static void main(String[] args) { Injector injector = Guice.createInjector(new BillingModule()); BillingService.factory = new BillingServiceFactory(injector); } ... // Deep in the bowels of my system. BillingService billingService = BillingService.factory.make(); billingService.processCharge(2034, "Bob");
Going into more detail about why this approach may be useful, Martin says this:
I like this because now all the Guice is in one well understood place. I don’t have Guice all over my application. Rather, I’ve got factories that contain the Guice. Guicey factories that keep the Guice from being smeared all through my application. What’s more, if I wanted to replace Guice with some other DI framework, I know exactly what classes would need to change, and how to change them. So I’ve kept Guice uncoupled from my application.
An interesting point made in clarification is that, in all examples provided, the BillingService itself adheres to the Dependency Injection principle; knowledge about what implementations of the BillingService's dependencies (CreditCardProcessor and TransactionLog) are used is externalized from the BillingService. To illustrate this, he concludes the article showing a JUnit test of the BillingService that uses simple hand-rolled "test doubles" of the TransactionLog and CreditCardProcessor, noting the test will run fine no matter what mechanism the application chooses to inject the dependencies at runtime.
(As a sidenote related to Martin's test doubles and explicit choice to "hand-roll" them, Gary Bernhardt has posted a very interesting article highlighting how this decision may be prudent in Java or other statically-typed languages, but not necessarily in dynamic languages such as Python.)
So, do you use Dependency Injection? Do you use a D.I. framework? If so, or also if not, how does all this resonate with you?
Community comments
Abstraction is good, abstraction is bad
by Jim Leonardo,
I wonder how often he does *coding* for the last couple of years
by monser corp,
Re: I wonder how often he does *coding* for the last couple of years
by Davy Herben,
Re: I wonder how often he does *coding* for the last couple of years
by Edward Moraru,
"I find annotations to do the same thing just as bad, btw"
by Chris Colman,
Abstraction is good, abstraction is bad
by Jim Leonardo,
Your message is awaiting moderation. Thank you for participating in the discussion.
The underlying principle here has nothing to do with DI. It's a more fundamental principle: hide your frameworks from your application so you can easily change them later. That's something I can throw at least a little support into.
However, let's be cautious. If the abstraction is there only for the sake of abstraction, it can drive you nuts if you are having to work on code originally written by someone else. Some frameworks suffer from this in that you have 5 or 6 levels of interface inheritance before you get to a concrete class and very little documentation in between to tell you how to create any of those concrete instances. Sure we all understand the idea of coding to interfaces, but sooner or later something needs to actually create an object instance. When that gets buried in all of the abstractions, the fun in life can disappear very quickly. It gets a bit like being told all the directions on the freeway, but never getting told where the on ramp is.
I wonder how often he does *coding* for the last couple of years
by monser corp,
Your message is awaiting moderation. Thank you for participating in the discussion.
IMHO, this is typical consultant behavior, picking up a new tech, play a little, blog to fame. In most of my projects using spring, I have only 1 class depending on spring: retrieving the context and start. 99.99% of my code does not know what spring or gucie is (unless I decide to use some lib/util from spring, in that sense, IOC container is no different from Log4J or else). If you really coupling your code to the container, you need to re-think about your design/implementation, instead of using another factory.
Re: I wonder how often he does *coding* for the last couple of years
by Davy Herben,
Your message is awaiting moderation. Thank you for participating in the discussion.
True, I'm seeing the same thing in my spring applications. The one place where I've got a spring dependency is in the main() method (or the web.xml, for that matter). It's exactly the 'God help us' xml configuration file(s) that makes sure that none of my code is spring dependent unless I want it to be.
I haven't looked at Guice, so I can't say if what Bob Martin is showing as an example is the right way to do things there. I would indeed feel reluctant to sprinkle DI framework code all over my code (I find annotations to do the same thing just as bad, btw). Just wondering if this is really needed in the first place. In spring, you could give every class access to the ApplicationContext and let them get the beans they want, but that's not what you want to be doing either...
Re: I wonder how often he does *coding* for the last couple of years
by Edward Moraru,
Your message is awaiting moderation. Thank you for participating in the discussion.
So, you've traded a dependency in a java class to a dependency in an XML file. To me, sounds the same.
I've worked with Guice and I had the Guice dependency in a couple of files not by using factories but a context that was accessible in the web layer.
The project used Stripes, Hibernate and Guice. Wonderfully minimalistic.
I don't like the factory per service, maybe it's more flexible that way.
It's a matter of style and taste, I think. To each it's own, then ;)
"I find annotations to do the same thing just as bad, btw"
by Chris Colman,
Your message is awaiting moderation. Thank you for participating in the discussion.
Amen to that! After decades of avoiding dependencies and 'lock in' to various technologies along comes annotations which infiltrate most domain classes like a virus, placing framework dependencies all throughout what *USED* to be POJO classes. IMHO once someone has placed vendor/framework specific annotations throughout their classes (i.e. other than @Override etc.,) they can't call their design POJO based anymore.
Object relational mappers (JPA/JDO) can do this to an otherwise POJO based domain model which is why I always use XML metadata for persistence (helps if it's autogenerated by a tool =] ).