Dependency Inversion of Control

Dependency Inversion of Control Principle with a Practical Example

SOLID OOP Wednesday, May 21, 2014

The last of SOLID principles suggests that the control of the lifetime of the unit of code dependencies is diverted to an external part of it. In other words, those dependencies are to be instantiated externally and given to the unit of work.

Follows an example commonly known to ASP.NET Webforms developers which is a particular application of inversion of control: the Template method pattern.

protected void Page_Load(object sender, EventArgs e)
{
}

In the previous example, the dependencies (sender and event arguments) are given to the developer "code territory" (the Page_Load event handler).

But what if the development team has dependencies of their own like a UserService. The easy solution was to instantiate one, why not? Not a good idea as that would break the "Program against abstractions, not concrete implementations" principle and it would prevent the unit of code from mocking or stubbing that dependency.

In the past few years, also aided by a more testable framework, ASP.NET MVC, another application of the inversion of control principle has become widely adopted: dependency injection. It may be applied in many forms: constructor injection, property injection, method injection and ambient context.

In the particular case of constructor injection, the class just asks asks for the dependency and an inversion of control container (commonly known as an IoC container) will inject those dependencies as constructor arguments.

public class HomeController : Controller
{
    public HomeController(IUserService userService)
    {
      // Assign to a private field.
    }
}

This is useful because is abstracts the connection between the high level class and the low level class, in this case, the controller and the user service, respectively.

What if you need a different implementation service? No problem, all you need to do is to register a the IUserService interface with another implementation.

Will you ever need to do that? What if you know that will never happen? Should you still use dependency injection? Yes, for 2 reasons:

  1. "Never" is a dangerous word for everything, not just programming. But, specially in programming, "always" is a safe word to use when assuming that the requirements always change. Your client, your manager, whoever may be, they'll never tell you in advance because they don't know their requirement will change. But it will so be prepared.
  2. Unit testing. If you don't have a way to stub or mock the user service (the low level class) into a new instance of the HomeController (or whatever high level class), that means your unit tests will never be unit tests. They'll be integration tests. Integration tests may be necessary but they serve a different purpose. If you don't have the possibility to change your low level class implementations, you'll be stuck with them and you will not have an automatic (and fast) way of knowing that a specific layer of your application is working as intended.