This article explains the concepts of Inversion of Control (IoC) and Dependency Injection (DI) in software development, using a simple example of a computer time tracker.
Abstract
Inversion of Control (IoC) and Dependency Injection (DI) are two important concepts in modern software development that can be confusing for some developers. The article explains these concepts using a simple example of a computer time tracker, where a Stopwatch instance is injected into the constructor of the ComputerTimeTracker class instead of being created inside the constructor. This change inverts the order of control, moving the responsibility to control the Stopwatch from the class to the user of the class. The article also highlights the benefits of using DI, such as loose coupling and easier unit testing.
Opinions
Inversion of Control (IoC) and Dependency Injection (DI) are often misunderstood and overhyped concepts in software development.
Dependency Injection is a simple concept that involves providing a dependency to someone who needs it instead of letting them create the dependency themselves.
Injecting dependencies via the class constructor, property, or setter-method are all valid ways of implementing DI.
Inverting the order of control by injecting dependencies can lead to looser coupling and easier unit testing.
Using an interface for the dependency instead of a concrete class can further enhance the benefits of DI.
The article suggests that understanding DI and IoC is important for every developer, as they enable the writing of readable unit tests for code.
The author invites readers to share concepts they don't understand and suggests that there are more benefits to DI and IoC that are not covered in the article.
How I Explained IoC and DI to Our Senior Software Engineer
Inversion of Control and Dependency Injection are two buzz words you always hear but may have never understood completely? Here is a tutorial for dummies!
Inversion of Control and Dependency Injection are two of these big buzz words you always hear when talking about modern software development. At least they were when I started going professional. That’s why I invested quite some time to research that topic. I was surprised our Senior Software Engineer did not…
I had to explain it to him while reviewing some of my code. I still remember his reaction:
That’s it? That easy? I was completely mystified by that stuff for years and you just explained it to me in a short call?
If even my valued colleague, who was my mentor for the past few years, still didn’t get that concept, I thought it might be worth to write yet another explanation:
Inversion of Control and Dependency Injection for Dummies!
Let’s say you want to write a piece of code that tracks how much time you spend in front of your computer. You might come up with a simple API like this:
If my colleague implemented that interface a few years back it would have looked somehow like that:
Simple and straight-forward. The only thing this class needs to do its work is a Stopwatch instance.
The class needs an instance of Stopwatch .
The class is dependent on a Stopwatch .
The Stopwatch is a dependency of the class ComputerTimeTracker .
So far, so good. Now let’s just do a little modification to that code:
The only thing that changed is the constructor.
Instead of creating the instance of Stopwatch inside the constructor we inject it into the constructor.
We inject the Stopwatch into the constructor.
We inject the dependency Stopwatch into the constructor.
We just did a dependency injection.
And I just want to make it clear: That is really all! Dependency injection means nothing more and nothing less than providing a dependency to someone who needs it instead of letting the someone create the dependency itself.
It does not matter whether you inject the dependency via the class constructor, via a property, or via a setter-method. All you do is injecting a dependency.
To quote my colleague again:
I think we have to discuss this. Now I need to create the Stopwatch instance before I can even create an instance of ComputerTimeTracker . That sucks. Isn’t that a code smell? Object oriented programming always praises information hiding, but now I have to know about a Stopwatch ? When I create the instance inside the constructor I just have to create my ComputerTimeTracker and it will handle the rest for me. I don’t want to know the implementation details, I just want to use the class. In summary: The Stopwatchshould be controlled by the ComputerTimeTracker .
Notice how he already used the word controlled. I’m just gonna think that thought a little bit further:
In the approach of my colleague we have clear order of control:
The class ComputerTimeTracker is controlling the Stopwatch .
It’s the responsibility of the ComputerTimeTracker to control the Stopwatch .
Now notice what changed when looking at the approach where we injected the Stopwatch :
We, the users of ComputerTimeTracker , are controlling the Stopwatch .
It’s the user’s responsibility to control the Stopwatch .
You might have already noticed it. By injecting the dependency we inverted the order of control. We moved the responsibility to control the Stopwatch from the class away to the user of the class. That is inversion of control. And again: That is really all!
Now that the buzz words are de-mystified we can talk about why we might prefer the approach with dependency injection. Let's enhance our sample code a little further:
All I changed is swapping the class dependency Stopwatch for an interface dependency IStopwatch . That way the class ComputerTimeTracker is not depending on the class Stopwatchof the .NET framework but loosely coupled to any instance that implements the interface IStopwatch . Now to our biggest benefit:
Unit tests!
All dependencies we inject into a class via an interface can be mocked. That is super convenient. Consider the following use case:
You start the tracking
You spend 42 hours before the computer
You stop the tracking
You check the spend time
Writing a unit test for that use case is hard when the Stopwatch instance gets created inside the constructor of ComputerTimeTracker . You can’t really replace it. And when you can’t replace it you can’t control the Stopwatch . So you would have to wait real 42 hours. With dependency injection you can simply inject a mocked Stopwatch into the class, just for the test.
Conclusion
Dependency injection and inversion of control are important concepts every developer should know. They are a little hyped and often misunderstood. But if you invest some time to really dig into it you see how dumb easy they actually are.
And why are they so hyped at all? For me just because they enable me to write readable unit tests for my code. This already sells it for me.
But there are more benefits I did not cover here: Dynamic replacement of dependencies or usage of an IoC-Container. But these are topics for a separate article…
Are there concepts that you just don’t get? Let me know! :-)