The terms "Inversion of Control" and "Dependency Injection" can sound a bit intimidating at first appearance. Even more confusing is the fact that they are closely related concepts. Once you grasp the underlying similarities, however, you'll find that implementing a non-trivial application using them can provide a number of benefits. This post is intended to distill these concepts down into a few takeaways to help you use them effectively.
What's the big deal?
A quick search of the above terms will yield many articles, some more long-winded than others. And honestly, many of the articles probably use the terms interchangeably, and in some cases, wrongly.
Let's not dwell on the meanings of the terms quite yet. The big takeaway behind the concepts is the idea of clearly separating the things a class depends on from the class itself. This can be achieved in a variety of ways, but typically the focus is on providing the dependencies to the class to use. The class need know nothing about where the dependencies came from; it just accepts them and uses them.
A simple example would likely help clarify the overall intent. Suppose you're writing a class that processes new customer orders. As part of this processing, a calculation needs to be done to determine when an order will be shipped. It will also need some mechanism to notify the customer of the ship date. Thus, we might implement the following:

Note that we've created a couple of interfaces, namely IOrderShipDateCalcuator and ICustomerNotifier, along with a couple of implementations of those interfaces. Our CustomOrderProcessor class then is given references to each of these interfaces via its constructor, which it then uses internally as needed. The CustomOrderProcessor class isn't responsible for deciding which specific implementation of the interface to use, and thus has no idea how the ship date is calculated or how the customer will be notified. More importantly, a user of the class can easily swap in a different implementation of the interface without impacting our CustomerOrderProcessor class.
Dependency injection is the term used to describe the “injection” of a dependency into an instance of another class. In this case, we might inject concrete implementations of IOrderShipDateCalculator and ICustomerNotifier into an instance of CustomerOrderProcessor through its constructor [1]. Consider the following code snippet that might use the code above:

A weakness with this code is that it must be modified, recompiled, and then redeployed each time a different dependency is used (e.g., going from EmailNotifier to TextMessageNotifier). This code could be made even more maintainable if the instantiation of CustomerOrderProcessor and its dependencies ICustomerNotifier and IOrderShipDateCalculator were done outside of the application, effectively “inverting control” relative to the traditional procedural programming flow used in the previous snippet.
The typical way this is done is through an Inversion of Control (IOC) Container [2]. The container is a piece of software that is configured with interface to class mappings. When asked to return an instance of class CustomerOrderProcessor, the container is smart enough to recognize that its constructor takes two classes implementing the interfaces IOrderShipDateCalculator and ICustomerNotifier, respectively. The container then checks its registry to determine which classes have been mapped to these interfaces, instantiates them, and passes them into the constructor for CustomerOrderProcessor. Using this approach, the snippet would be transformed to look something like this [3]:

The beauty of this technique is that the CustomerOrderProcessor class doesn't really care where the implementations of its dependencies came from or about the underlying details of those implementations. The class can just be concerned with doing what it needs to do to process the order and use those interface references to help it do its work.
Sounds interesting. So, what does this approach buy me?
First and foremost, unit testing becomes considerably easier. In our example above, suppose the ProcessOrder method explicitly newed up an instance of our EmailNotifier. If we were to try to unit test that method, we'd be sending out an email each time we run the test. With our current approach, we can have our unit test instead pass in a "mock" or "fake" implementation of the ICustomerNotifier which, for example, just writes the message to the console. In that way, our unit test can focus explicitly on what's going on in our class, instead of caring about what's going on in dependencies of our class.
Let's consider again the ease by which we can now swap in a separate implementation of an interface. This allows us to make enhancements to our application often with minimal or even no impact. For example, suppose we add some additional points of contact to our Customer class above. We could build an implementation of ICustomerNotifier that deals with this occurrence seamlessly from the point of view of the CustomOrderProcessor. Another common way to take advantage of this type of interface usage is to inject a caching layer in front of database access to improve application performance. If things are done right, the decision of which implementation of an interface to use occurs in just one place (the IOC container), and thus the swapping process is quite painless.
Any best practices?
The discussion thus far obviously relies on the use of interfaces, which can take a little getting used to if you're not doing it already. The following tips should help in this endeavor:
- In general, keep your interfaces small. If an interface tries to do too many things, it becomes more challenging to swap out one implementation for another. For example, an "order service" interface having methods to create, cancel, process, retrieve, and update orders can be cumbersome to swap if we ultimately only want to change how we retrieve orders. If we had a separate interface to retrieve orders from that to manipulate orders, things would be easier [4].
- Use some discretion when deciding where an interface is truly needed. Obviously we can't always foresee what the future holds for our code. Think of your application as building blocks that fit together. If your building blocks are too small, the code will become difficult to read and understand as you'll be wading through interface after interface. If your building blocks are too big, the code will be more difficult to change and evolve as needs inevitably change.
- Try to keep interfaces relatively generic. In the example above, we could have backed ourselves into a corner by defining our ICustomerNotifier interface in a way that assumes the notification method would always be email-driven. Instead, we kept it at a higher level, allowing us to use it for any kind of future notification approach that might arise (e.g., text messaging).
Wrapping Up
Hopefully this post has made the concepts of "inversion of control" and "dependency injection" a bit less nebulous. We saw the benefits of "injecting" dependencies into a class, thus "inverting" control over instantiating the dependencies of a class from the class itself to instead elsewhere in the application. When a class no longer has to instantiate its dependencies, many doors are opened in easily extending your application when needed in the future. While this does require a bit more thought in the design process, the results are definitely worth the extra effort.
Footnotes
[1] There are other ways to inject dependencies as well, but constructor injection is likely the most common among them.
[2] In some circles, these are known as Dependency Injection containers, or even Dependency Inversion containers as well. This further exhibits the extent of the overlap between the concepts of Inversion of Control and Dependency Injection.
[3] The particulars in configuring the IOC Container and retrieving instances from it vary depending on the container. Some capable IOC containers include StructureMap, NInject, and Unity; each has a bit different syntax and capabilities.
[4] As an interesting aside on this, check out the command-query separation pattern.