BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Decoupling Your Application From Your Dependency Injection Framework

Decoupling Your Application From Your Dependency Injection Framework

Leia em Português

This item in japanese

Bookmarks

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?

Rate this Article

Adoption
Style

BT