Before we dig in too deeply to Dependency Injection (DI), we need to ask an important question: Should we even be doing this in the first place?
There are arguments for and against using DI and DI containers, and considerations of when to use it versus when not to use it. This post will deal with the advertised advantages of DI. In the spirit of objectivity, some disadvantages and consideration of them will be in a follow-up post.
There are four main advantages I will be dealing with here:
- Link using class to an interface rather than an implementation
- Facilitate testable code
- Manage scoping rules for your object graph
- Reduce boilerplate code
Instantiating members inside a parent class permanently links the parent class to an implementation instead of an interface. Now one might counter that when declaring a member, assigning an implementation to an interface keeps the parent class from being linked to the member’s implementation. However, the parent class itself still depends knowing that the concrete class exists to assign the member. If this assignment is made in 100 different classes and you want to change the signature of the implementation’s constructor, you will quickly find that parent class really is tied to the implementation instead of the interface.
Injecting dependencies facilitates testable code because small portions of your application are easy to instantiate. You can instantiate the subject under test directly, specify mock objects or stubs for its dependencies (things that are not easy to instantiate, or things that have side effects like network calls or database calls) and unit test to your heart’s content! When the dependencies are hard-wired inside the subject under test, testing like this becomes impossible and you are reduced to integration tests, or worse, manual tests. The advantages of unit testing are left for another blog entry…
The DI Container can easily manage complex scoping rules. If you have portions of your application’s object hierarchy that need to be instantiated only for certain scopes (say, for a single request, or one per thread – request scope and thread scope) the DI container will manage the instantiations at the right time rather than your code having to manage it. Guice provides session scope, request scope and custom scopes, and Spring 3.x provides a thread scope in addition to its other scopes.
DI Containers reduce the boilerplate of setting up dependencies. You can provide a dependency to potentially many client classes rather than each client class instantiating it. This way you do not have to repeatedly write the code to do additional work that a dependency needs before it can be used (like calling setters, calling initialize methods, etc). You can also avoid writing the code to wire other dependencies that the original dependency might have. The container will provide the entire hierarchy of dependencies with the appropriate scope.
I think these four points (Link client class to an interface rather than an interface, Facilitate testable code, Manage scoping rules for your object graph, Reduce boilerplate code) are good arguments for using a DI container. In the next post we will look at arguments against using a DI container.