Dependency injection is sometimes confused with dependency inversion. However, the former is only an implementation detail for both dependency inversion and inversion of control.
Dependency injection is a useful technique to introduce dependencies in your code. It might be one class we want to use in another one, or it might be a service from an external library that we want to call in our controller. And it is the way to make dependency inversion possible.
However, dependency inversion is the term of a higher order. Now we talk about dependencies in a broader scope, not about concrete classes, but about abstractions behind them. This time we want to use
TimeInterface in our
PublishingInterface, but not about the
DateTime class (or
time function) in the
FacebookPublisher class. Using interface and class terms here, I want to oppose concrete implementation versus abstractness. We usually solve the coupling problem in our architecture by using the dependency inversion.
When we think about the inversion of control, we actually stop considering dependencies, and start working with the control flow. It is about keeping uncertainty as long as possible. Keeping that uncertainty, we approach decoupling and code reusability.
Dependency injection is a way to approach dependency inversion in a clean manner, and also one of many options when you think of how to apply the inversion of control in your architecture.