Dependency Injection Inversion

Posted by Uncle Bob on 01/17/2010

Dependency Injection is all the rage. There are several frameworks that will help you inject dependencies into your system. Some use XML (God help us) to specify those dependencies. Others use simple statements in code. In either case, the goal of these frameworks is to help you create instances without having to resort to new or Factories.

I think these frameworks are great tools. But I also think you should carefully restrict how and where you use them.

Consider, for example, this simple example using Google’s Guice framework.

 public class BillingApplication {    public static void main(String[] args) {     Injector injector = Guice.createInjector(new BillingModule());     BillingService billingService = injector.getInstance(BillingService.class);     billingService.processCharge(2034, "Bob");   }  }

My goal is to create an instance of BillingService. To do this, I first get an Injector from Guice. Then I use the injector to get an instance of my BillingService class. What’s so great about this? Well, take a look at the constructor of the BillingService class.

class BillingService {   private CreditCardProcessor processor;   private TransactionLog transactionLog;    @Inject   BillingService(CreditCardProcessor processor, TransactionLog transactionLog) {     this.processor = processor;     this.transactionLog = transactionLog;   }    public void processCharge(int amount, String id) {     boolean approval = processor.approve(amount, id);     transactionLog.log(       String.format("Transaction by %s for %d %s",       id, amount, approvalCode(approval)));   }    private String approvalCode(boolean approval) {     return approval?"approved":"denied";   } } 

Oh ho! The BillingService constructor requires two arguments! A CreditCardProcessor and a TransactionLog. How was the main program able to create an instance of BillingService without those two arguments? That’s the magic of Guice (and of all Dependency Injection frameworks). Guice knows that the BillingService needs those two arguments, and it knows how to create them. Did you see that funky @Inject attribute above the constructor? That’s how it got connected into Guice.

And here’s the magic module that tells Guice how to create the arguments for the BillingService

public class BillingModule extends AbstractModule {   protected void configure() {     bind(TransactionLog.class).to(DatabaseTransactionLog.class);     bind(CreditCardProcessor.class).to(MyCreditCardProcessor.class);   } } 

Clever these Google-folk! The two bind functions tell Guice that whenever we need an instance of a TransactionLog it should use an instance of DatabaseTransactionLog. Whenever it needs a CreditCardProcessor it should use an instance of MyCreditCardProcessor.

Isn’t that cool! Now you don’t have to build factories. You don’t have to use new. You just tell Guice how to map interfaces to implementations, and which constructors to inject those implementations in to, and then call Injector.getInstance(SomeClass.class); and voila! You have your instance automatically constructed for you. Cool.

Well, yes it’s cool. On the other hand, consider this code:

public class BillingApplicationNoGuice {   public static void main(String[] args) {     CreditCardProcessor cp = new MyCreditCardProcessor();     TransactionLog tl = new DatabaseTransactionLog();     BillingService bs = new BillingService(cp, tl);     bs.processCharge(9000, "Bob");   } } 

Why is this worse? It seems to me it’s better.

But Uncle Bob, you’ve violated DIP by creating concrete instances!

True, but you have to mention concrete instances somewhere. main seems like a perfectly good place for that. Indeed, it seems better than hiding the concrete references in BillingModule.

I don’t want a bunch of secret modules with bind calls scattered all around my code. I don’t want to have to hunt for the particular bind call for the Zapple interface when I’m looking at some module. I want to know where all the instances are created.

But Uncle Bob, You’d know where they are because this is a Guice application.

I don’t want to write a Guice application. Guice is a framework, and I don’t want 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. I don’t want to have @Inject attributes everywhere and bind calls hidden under rocks.

But Uncle Bob, What if I want to get an instance of BillingService from deep in the bowels of my application? With Guice I can just say injector.getInstance(BillingService.class);.

True, but I don’t want to have createInstance calls scattered all through my code. I don’t want Guice to be poured all over my app. I want my app to be clean, not soaked in Guice.

But Uncle Bob, That means I have to use new or factories, or pass globals around.

You think the injector is not a global? You think BillingService.class is not a global? There will always be globals to deal with. You can’t write systems without them. You just need to manage them nicely.

And, no, I don’t have to use new everywhere, and I don’t need factories. I can do something as simple as:

public class BillingApplicationNoGuice {   public static void main(String[] args) {     CreditCardProcessor cp = new MyCreditCardProcessor();     TransactionLog tl = new DatabaseTransactionLog();     BillingService.instance = new BillingService(cp, tl);      // Deep in the bowels of my system.     BillingService.instance.processCharge(9000, "Bob");   } } 

But Uncle Bob, what if you want to create many instances of BillingService rather than just that one singleton?

Then I’d use a factory, like so:

public class BillingApplication {    public static void main(String[] args) {     Injector injector = Guice.createInjector(new BillingModule());     BillingService.factory = new BillingServiceFactory(injector);      // Deep in the bowels of my code.     BillingService billingService = BillingService.factory.make();     billingService.processCharge(2034, "Bob");   } } 

But Uncle Bob, I thought the whole idea was to avoid factories!

Hardly. After all, Guice is just a big factory. But you didn’t let me finish. Did you notice that I passed the Guice injector into the factory? Here’s the factory implementation.

public class BillingServiceFactory extends AbstractModule {   private Injector injector;    public BillingServiceFactory(Injector injector) {     this.injector = injector;   }    protected void configure() {     bind(TransactionLog.class).to(DatabaseTransactionLog.class);     bind(CreditCardProcessor.class).to(MyCreditCardProcessor.class);   }    public BillingService make() {     return injector.getInstance(BillingService.class);   } } 

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.

Indeed, using this form allows me to defer using Guice until I think it’s necessary. I can just build the factories the good old GOF way until the need to externalize dependencies emerges.

But Uncle Bob, don’t you think Dependency Injection is a good thing?

Of course I do. 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.

BTW, did you notice that I was using Dependency Injection even when I wasn’t using Guice at all? This is nice and simple manual dependency injection. Here’s that code again in case you don’t want to look back:

 public class BillingApplicationNoGuice {   public static void main(String[] args) {     CreditCardProcessor cp = new MyCreditCardProcessor();     TransactionLog tl = new DatabaseTransactionLog();     BillingService bs = new BillingService(cp, tl);     bs.processCharge(9000, "Bob");   } } 

Dependency Injection doesn’t require a framework; it just requires that you invert your dependencies and then construct and pass your arguments to deeper layers. Consider, for example, that the following test works just fine in all the cases above. It does not rely on Guice, it only relies on the fact that dependencies were inverted and can be injected into BillingService

 public class BillingServiceTest {   private LogSpy log;    @Before   public void setup() {     log = new LogSpy();   }    @Test   public void approval() throws Exception {     BillingService bs = new BillingService(new Approver(), log);     bs.processCharge(9000, "Bob");     assertEquals("Transaction by Bob for 9000 approved", log.getLogged());   }    @Test   public void denial() throws Exception {     BillingService bs = new BillingService(new Denier(), log);     bs.processCharge(9000, "Bob");     assertEquals("Transaction by Bob for 9000 denied", log.getLogged());       } }  class Approver implements CreditCardProcessor {   public boolean approve(int amount, String id) {     return true;   } }  class Denier implements CreditCardProcessor {   public boolean approve(int amount, String id) {     return false;   } }  class LogSpy implements TransactionLog {   private String logged;    public void log(String s) {     logged = s;   }    public String getLogged() {     return logged;   } } 

Also notice that I rolled my own Test Doubles (we used to call them mocks, but we’re not allowed to anymore.) It would have been tragic to use a mocking framework for such a simple set of tests.

Most of the time the best kind of Dependency Injection to use, is the manual kind. Externalized dependency injection of the kind that Guice provides is appropriate for those classes that you know will be extension points for your system.

But for classes that aren’t obvious extension points, you will simply know the concrete type you need, and can create it at a relatively high level and inject it down as an interface to the lower levels. If, one day, you find that you need to externalize that dependency, it’ll be easy because you’ve already inverted and injected it.

Comments

Leave a response