C#
Sam Lau  

Explain Dependency Injection Like I am Five

If you read posts about .NET or Microsoft’s documentations, you see the parse “dependency injection” (DI) a lot. Everyone say it is good practice and you should do it but what exactly is DI? Today, I will try to explain dependency injection like you are five.

Dependency Inversion

Before talking about dependency injection, let’s talk about another DI, “dependency inversion”. The main goal of dependency injection is archiving dependency inversion. Let’s explain dependency inversion with a real world example. Imagine you want to drink a coffee. One way to do it is to grow your own coffee plants, harvest the beans, roast the beans, grind the beans, and make coffee from the coffee grind.

This is direct dependency. Your coffee depends on the coffee grind. Coffee grind depends on coffee beans and coffee beans depends on coffee plants. You need to know every steps in the chain to make coffee.

Of courses, no one does this. You go to a coffee shop for your coffee. You tell the cashier if you want Americano and you don’t care how it is made. The coffee shop probably doesn’t roast their coffee beans. They tell the roastery what types of beans they want and just buy it from them. The roastery doesn’t grow coffee plants. They buy raw beans from coffee farm. You get what I mean.

Instead of you knowing how to make a coffee, you just need to know what is an Americano. The barista knows how to make an Americano. Americano is the contract or what we call interface in software. The dependency is now inverted. Instead of your cup of coffee depends on you grinding the coffee, the barista depends on the contract of an Americano. That’s the heart of dependency inversion. The implementation depends on the contract for what it should implement. The consumer depends on the contract instead of directly depending on the implementation.

The benefit of such design is very obvious using the coffee example. You can switch coffee shop easily without the hassle of growing different coffee plants. In software terms, you can easily switch the implementation details as long as the interface does not change. This also makes testing a lot easier because you can easily substitute a mock with the same interface.

Here is the Microsoft’s documentation on dependency inversion with graphs. I hope my explanation makes Microsoft’s dependency graph make more sense.

Dependency Injection

After understanding what is dependency inversion. Dependency injection become really simple. It is just a way to archive dependency inversion in code. Instead of creating and calling the dependency directly in your code, you ask for an interface of your dependency (inject the dependency) and use it.

Basically, instead of this:

public class MyClass() : BackgroundService
{
    public void Run()
    {
        CoffeeMaker maker = new CoffeeMaker();
        maker.MakeCoffee();
    }
}

You do this (given CoffeeMaker : ICoffeeMaker):

public class MyClass(ICoffeeMaker maker) : BackgroundService
{
    public void Run()
    {
        maker.MakeCoffee();
    }
}

Then, where do we actually create the CoffeeMaker as we are only requesting an ICoffeeMaker in the constructor? Here is where .NET built-in service container come in the place. You just need to add one line to Program.cs, something like:

builder.Services.AddSingleton<ICoffeeMaker, CoffeeMaker>();

and the framework will handle the object creation for you. There are more to it like service lifetimes but for that you should read more on Microsoft’s documentation.

Conclusion

I hope this post give you a simple understanding of what is dependency injection and more dependency inversion. This is a technique that will help you a lot in the .NET journey and I hope you can master it.