Dependency Inversion is a widely used principle in building software today. While it is heavily used, the understanding of Dependency Inversion, Inversion of Control and Dependency Injection are less well known. These three principles and patterns are often spoken about as if they are the same.
In this post, I will cover each of the three terms, Dependency Inversion, Inversion of Control and Dependency Injection and look at what each of them are and how they are different from each other.
This is the D in the SOLID principles (https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)). The first important fact about Dependency Inversion, is that it is a principle and not a pattern.
Dependency Inversion can be seen as a high level look at what should be achieved, then what it would look like once we have achieved it.
The main idea is to not write tightly coupled code. Dependency Inversion as the name implies inverts the control by giving control to higher level parts (classes) of your system.
To do this, first ensure that the higher level parts of your application have been decoupled from the lower level. You must know what serves as high level and low level components of your system. We can achieve this by creating separate libraries and/or user interfaces.
When code is tightly coupled, it gets harder to maintain the bigger your application gets.
What does it mean when we say, parts of your system should be loosely coupled? Loosely coupling is where we look to reduce the dependency of a class that uses a different class directly. When we have something tightly coupled, it is seen as bad and makes it harder to change, a simple change to a single class could have major impact on many other classes that rely on the class you are changing. The re-usability of the code is reduced and it is often harder to test.
To summarize Dependency Inversion principle is a higher overarching principle that we are going to use to guide how we develop our software. We don’t want higher level modules depending on lower level modules.
The following image shows when we have not inverted the dependency from the lower level
Dependency not Inverted
The next image shows us how it should look if we have correctly Inverted Dependency to the Higher Level Class
Inversion of Control
Inversion of control is a design principle that looks to invert the control of something by switching who controls it. Inversion of control means we are switching it from the normal way, where a class might initialize other classes from within the class and look to move it out into another higher class.
Inversion of control and dependency injection are all about removing dependency from your code. Anytime you take the creation of a dependency or the binding of that dependency and you are moving it outside of the current class, you are inverting the control of the creation of that dependency.
To summarize, inversion of control is a principle, that has a set of prescriptions for how we can achieve this principle. It is a way of taking control points and inverting them for the benefit of reducing dependencies or simplifying our code. Martin Fowler used the term Hollywood Principle, as in Don’t call us, we’ll call you.
Remember that If you want to prevent a high level module from depending on a low level module, you will have to invert the control so that the low level module is not controlling the interface and the creation of the objects that the high level module needs.
Dependency injection is a design pattern, it uses the Inversion of Control principle to guide how we are actually going to implement Inversion of Control.
We will take the dependencies that are sitting in lower level classes, then instantiate it outside of the class. We would normally instantiate in a higher level class, that other lower level classes can access to get that dependency.
DIfferent ways to Implement Dependency Injection
Constructor injection is one of the most common ways to achieve dependency injection. We would take a dependency and pass the dependency into the class via a constructor. We can do this by newing up a class outside of the class that we want to inject it into, we would then pass it into the constructor of the class that we want to inject it into.
A common way is to use an IoC container to house all of your dependencies in one place, allowing for easy modification and creation of the dependencies ready to be passed and used in other classes.
The setter injection simply instantiates the dependency inside the setter of a property. You need to be careful when using the setter as your dependency injection, it is possible to create and even use a class before it has had the dependency set.
The interface injection is similar to the setter injection. The idea with the interface injection is we are going to define the method that’s going to be used to do the injection.
To use interface injection, you would create a special interface that will have a method that will inject the dependency. So then our injector is just going to simply use that interface to set the dependency.
Why would we want to achieve Inversion of Control?
Some advantages of why we would want to achieve Inversion of Control are:
- Gives you the ability to control functionality from a central place, rather than it spread throughout your application.
- It gives you the ability to more easily test each class in isolation, by allowing the use of mocks or the ability to create fake objects.
Dependency Injection drawbacks
- Dependency Injection can increase complexity, it does this by increasing the number of classes due to the fact that we are separating responsibilities. Depending on what you are developing might not always be beneficial.
- Your code will be somewhat coupled to the dependency injection framework that you are using.
- The DI Containers that are often used will have a performance hit at start-up. This is small, but worth mentioning, that there is some performance lost at the start.
We have looked at three parts, Dependency Inversion principle, Inversion of Control and Dependency Injection.
Knowing what each of these means and how to achieve the Dependency Inversion principle is a really important part to C# development, it helps to avoid tightly coupled code, it allows you to more easily unit test your methods and in some cases simplifies your code.