Introduction to Dependency Injection

One of the core concepts of the Spring Framework is Dependency Injection (DI, or Inversion of Control). Although Spring uses this concept, it is incorrect to think that Dependency Injection is just a Spring thing. So, if Dependency Injection is so important, then why have I waited this long to talk about it? It is an easy concept to understand. But you will understand why it is so important now that we have the context of an example application. Besides, we are closer to the testing chapters and one of the main points of DI is that it makes testing easier.

Some myths about Dependency Injection…

Dependency Injection is another overhyped methodology that makes life difficult and is not really necessary, anyway.
The only benefit of Dependency Injection is to make testing easier.
Dependency Injection is impossible without interfaces.
Dependency Injection requires the Spring Framework.
Dependency Injection requires lots of xml configuration.
Dependency Injection is necessary for every dependency.

What is a Dependency?

de·pend·en·cy noun, plural -cies. Relationship between conditions, events, or tasks such that one cannot begin or be-completed until one or more other conditions, events, or tasks have occurred, begun, or completed.

In java, a class (call it class A) is dependent on another class (call it class B) if class A needs an instance of class B in order for class A to do something.

public class CopyService {   
    private Keyboard keyboard = new Keyboard();  // class CopyService is dependent on class Keyboard
    private Printer printer = new Printer();     // class CopyService is dependent on class Printer  
 
    //now class Copy does something with the dependencies
    public String copy() {
        keyboard.read();
        printer.write();
        return "Copying finished";
   }
}

What is wrong with this code? Well, there is nothing wrong with class CopyService needing dependencies for it’s copy() method. All code is full of dependencies. Lots of them. There are in fact 2 things wrong with this code. The first problem is the way CopyService gets its dependencies, which makes it difficult to test.

Imagine testing this class. For general testing, you need to set up a testing environment and you do not have access to Keyboard and Printer because they access databases or systems that aren’t available for testing. So you would like to mimic the Keyboard and Printer in some way so that you can just test the copy() method.

If only we had created a constructor in CopyService to initialize the dependencies, then testing would be easier. We would create a subclass of Keyboard and Printer for testing purposes only, and override the methods we want.

Dependency Injection… the “constructor” way

public class CopyService {
   private Keyboard keyboard;
   private Printer printer;
   public CopyService(Keyboard keyboard, Printer printer){
      this.keyboard = keyboard;
      this.printer = printer;
   }
   public String copy() {
     ....
   }
}
 
public class CopyServiceTest extends TestCase {
 
  public void testCopy() {
     Keyboard fakeKeyboard = new FakeKeyboardSubclass(); //we extended Keyboard to override the read() method
     Printer fakePrinter = new FakePrinterSubclass(); //we extended Printer to override the write() method
     CopyService copyService = new CopyService(fakeKeyboard, fakePrinter);
     String expectedResult = copyService.copy();
     assertEquals(expectedResult, "Copying finished"); 
  }
 
}

There, we used a constructor in the CopyServiceTest to “inject” dependencies! And we haven’t even used interfaces yet. Dependency injection is really that simple. The “constructor way” is not the only way to achieve dependency injection. It can also be done with setter methods.

Dependency Injection… the “setter” way

public class CopyService {
   private Keyboard keyboard;
   private Printer printer;
   public void setKeyboard(Keyboard keyboard) {
      this.keyboard = keyboard;
   }
   public void setPrinter(Printer printer) {
      this.printer = printer;
   }
   public String copy() {
     ....
   }
}

now testing would be done like this:

  public void testCopy() {
     Keyboard fakeKeyboard = new FakeKeyboardSubclass(); //we extended Keyboard to override the read() method
     Printer fakePrinter = new FakePrinterSubclass(); //we extended Printer to override the write() method
     CopyService copyService = new CopyService();
     copyService.setKeyboard(fakeKeyboard);
     copyService.setPrinter(fakePrinter);
     String expectedResult = copyService.copy();
     assertEquals(expectedResult, "Copying finished"); 
  }

Constructor Injection vs. Setter Injection

Which is better, constructor based dependency injection or setter based injection? There are pros and cons to each way. I would argue that constructor based dependency injection is less error prone, because you are imposing certain rules and telling your class which dependencies its methods needs. More importantly, constructor injection enforces the order of initialization and prevents circular dependencies. With setter injection it is not clear in which order things need to be instantiated and when the wiring is done.

On the other hand, there are reasons why setter injection wins. Unit testing is always easier if constructors are simple and do less, or do nothing at all. Besides, not all methods of a class require the same dependencies. If you use the Spring container to inject your dependencies, then you don’t always have to worry about this because the Spring container will automatically call your setters for you (setKeyboard and setPrinter) if you configure it correctly. We will get to that later.

Abstractions make code flexible and reusable

So, things are looking better now for our CopyService class. But there is still another problem with this code! It is not flexible or reusable. One day we run out of printer paper and we decide we would like to write documents to a digital pdf file. So, we create a new class called PdfWriter. Now we have to stop the application, replace references to “Keyboard” with “PdfWriter”. Then we redeploy and restart the system. We lost a few customers during the wait, but most of them were patient and didn’t mind.

Wouldn’t it be better to avoid this scenario? Is there a way to reuse CopyService to magically turn it into a digital printing service without having to change the class? What if we could have both Printer and PdfWriter and could easily switch back and forth between the two whenever we wanted? We wouldn’t need to change the 200 references to Keyboard in the different Service classes across the rest of the application, and best of all, we would not even need to redeploy and restart the application. We create an abstraction for the Keyboard and Printer classes by creating interfaces called “Reader” and “Writer”. Now CopyService class goes about its business without even knowing what instance of Reader and Writer it has. Reader could be KeyboardReader or VoiceReader for all it knows, and Writer could be PrinterWriter or PdfWriter.

public class CopyService {
 
     private Reader reader; // interface Reader could be implemented by KeyboardReader, VoiceReader, etc...
     private Writer writer; // interface Writer could be implemented by PrinterWriter, PdfWriter, etc...
 
     //constructor style injection...
     public Copy(Reader reader, Writer writer) {
         this.reader = reader;
         this.writer = writer;
     }
 
     //...or setter style injection
     public void setReader(Reader reader) {
        this.reader = reader;
     }
     public void setWriter(Writer writer) {
        this.writer = writer;
     }
 
     public void copy() {
        reader.read();
        writer.write();
     }
 
}

Cool…. now here is the best part. Using a container like Spring allows us to switch implementations on the fly, during runtime. You would just need to configure in an xml file somewhere on the file system something like the following:

<bean id="copy" class="CopyService">
    <constructor-arg ref="reader" />
    <constructor-arg ref="writer" />
</bean>
 
<bean id="reader" class="KeyboardReader" />
<bean id="writer" class="PrinterWriter" />
 
<!-- switch to this alternate implementation whenever you want...
<bean id="reader" class="VoiceReader" />
<bean id="writer" class="PdfWriter" /> 
-->

This is also great for testing! When testing, you can create mock implementations of classes and swap them in and out as you wish. Though it is certainly possible, you don’t always want to create mock subclasses for testing. It is cleaner with interfaces. Then you can create an implementation class called FakePrinter and swap it in with your configuration. There are even frameworks that help you do this. Check out mockito, easymock, and jmock. More on that later.

Spring is the most widely used container but not the only one for dependency injection. There is also the PicoContainer and Hivemind to name a few.

As of Spring 2.5, it is also not necessary to define all beans in an external xml file anymore. Dependencies can be configured with Spring’s @Autowired annotation or @Inject from javax. There’s more on that in the configuration section of these tutorials.

No more bad code!

Our bad code has now significantly improved. Bad code has a definition, thanks to Robert C. Martin who wrote a paper in 1994 about “The Dependency Inversion Principle”. He defined “bad code” to have the following three defining factors:

1. It is hard to change because every change affects too many other parts of the system. (Rigidity)
2. When you make a change, unexpected parts of the system break. (Fragility)
3. It is hard to reuse in another application because it cannot be disentangled from the current application. (Immobility)

According to Martin, this can be remedied by applying the following principle:

Dependency Inversion Principle (DIP)

1. HIGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW LEVEL MODULES. BOTH SHOULD DEPEND UPON ABSTRACTIONS.
2. ABSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS SHOULD DEPEND UPON ABSTRACTIONS.

In the beginning, our code was bad. It was rigid and fragile because we had concrete references to classes Keyboard and Printer. Any changes or problems in these “lower level” components would affect “higher level” components such as CopyService and all other classes that use these dependencies. We still have this risk even after improving the code, however when dependencies are isolated, then testing is easier and with better tests, problems are identified faster. In the beginning our code was immobile because it was not possible for CopyService to switch back and forth between implementations of Reader and Writer. Its only choice was Keyboard and Printer, which were hard coded into the CopyService class. By abstracting away the implementation details, we made the code reusable and flexible.

We have followed the Dependency Inversion Principle and improved our code. We changed CopyService to depend on abstractions of low level modules (Reader and Writer). And, class PrinterWriter which has the implementation details, depends on the Writer interface abstraction for its implementation. Since the Writer interface imposes certain rules on the PrinterWriter implementation, a dependency is introduced.

A word of caution…

Now that we see how easy dependency injection is, we aren’t going to go wild with it just yet. While dependency injection is very useful, one of the greatest challenges is knowing when and how to use it correctly. There are several ways to do it, and choosing the wrong way could result in hundreds of needless interfaces or thousands of lines of xml configuration code that is difficult to refactor. Dependency injection is not necessary for every single dependency in the application. How and when you create interfaces and configure all your dependencies is something that should require a significant amount of thought. That is what the next section is about, so keep reading!

References

This example was inspired by this paper.
For a more complicated example, refer to the wiki.

1 Comment
Thomas wrote on 2012-02-13 at 10:14:19:
Hey Althea, This piece is really insightful and enlightened my understanding of DI. I have also read how this is done in a PHP framework (maybe for a later comparison between Java and PHP? ;) http://flow3.typo3.org/documentation/guide/partiii/objectmanagement.html#object-dependencies Best, Thomas


Post a Comment

(required):