Dependency injection is a programming technique that helps programmers be more productive. If you are writing a program that needs to sort a list, rather than writing the list-sorting code yourself, you might instead write a statement that pulls in list-sorting code from somewhere else (the “somewhere else” is “injecting” the code you “depend” on), and you just use that.
It just means you compose functionality. The human body is a composite of organs, for example. Whenever your body needs something, it delegates tasks based off of functionality to the systems and subsequent organs responsible. It communicates these instructions via signals from the top down, rather than the other way around. This is what defines the composition.
When you need to breathe, a part of your brain will tell your lungs to expand, and they pull in air. Some passive systems will pick up the result from breathing in, and eventually affect how your entire body feels.
Most programming is pretty linear — when you call a function, you pass in what the function needs. If a function takes parameters X, Y, and Z, you provide those.
With dependency injection you flip this on its head. First you declares somewhere, “hey if you need an X, I’m how you get it”. Similarly for Y and Z. Then when the function is called, you don’t give it anything — instead, it knows it needs an X, and it looks in the registered providers for how to get one. Then it gets it.
It’s basically a way to turn nice predictable flows into a horrible rat’s nest of untraceable dependencies which are impossible to debug sensibly.
It’s a modern architecture choice.
In “normal” code, or the intuitive way people are taught, if a part of the code needs another thing, it is responsible for creating or getting that thing.
So, for example, if I’m writing code that calculates the total for an invoice, maybe I also want some code that knows how to calculate the tax for each item. That tax code is “a dependency” because my “bigger” piece of code depends on it.
In really old programs, all of that code would be in more or less the same place. But for about 60 years now code has been separated into what I will oversimplify and call “modules” so that bits of it can be used and reused in many parts of a program and/or tested by itself.
So in my code, I’d have a tax calculation module and my invoice calculation module depends on it. Without dependency injection, I’d do something language specific to make my invoice calculation code refer to the module. For example, in an OOP language I’d “create a tax calculator object”.
But WITH dependency injection, I’d instead write my invoice calculation module in a way that says, “I need someone ELSE to give me a tax calculation module to use.” That “someone else” is the thing that “injects” the dependency.
In physical terms, it’s like the difference between buying a computer and building a computer. Buying a computer is not having Dependency Injection. You get a pre-assembled computer. In some cases, you can DIY replace parts, but in cases like MacBooks you get what you get. Dependency Injection is more like building your own computer: you get to pick and choose each part and you know you’ll always be able to swap different parts in.
In practical terms it’s supposed to make large-scale programs a little easier to maintain by making them behave more like DIY computers than MacBooks. That way in theory if things change, the developer only has to change some “dependencies” and maybe not the modules that depend on them.
But there are some snarky people in this thread claiming it doesn’t work. What they’ve encountered is that it’s *hard* to write software that can be changed without a lot of effort. A lot of people have that attitude. My experience is those people don’t tend to work on software they have to keep changing for 15 years, so they don’t value practices that make very long-term maintenance easier at the cost of very short-term complexity. You can make a bigger mess with DI if you aren’t thinking hard. If you aren’t thinking hard you shouldn’t be programming.
That’s what architecture is in software: trading one kind of complexity for another we hope is better.
It makes it simpler to deal with dependencies that have dependencies of their own. Let’s use a database access object (DAO) as an example. To create the DAO, you need to tell it how to to access the database. If you need to use this DAO in another part of the program, then that file also needs to know how to access the database in order to initialize the DAO.
Instead of doing that, you can initialize the DAO and designate it as an injectable object, and then everywhere else in the program where you need to use that DAO, you just indicate that it needs the DAO and the dependency injection adds it in during initialization.
Latest Answers