Dependency injection is a way to arrange software in such a way that you say what you need, but not where to get it from or how to get it. It is at the core, a shopping list of things you expect to receive, without worrying about how to get to the store, which specific things that were on your list you get.
You may have heard of dependency inversion principle, where you can depend on interfaces in a constructor, and pass anything fulfilling that dependency higher-up the chain.
Dependency injection takes this one step further by providing containers, which register either concrete dependencies instantiated at boot-time, late-instantiated at call-time, shared between components; or late-bound with the ability to build or retrieve what you need when you need it.
Time was this could only be done in heavy type-strict modern languages such as Java, which often uses what seem like mystical
@Bean annotations (A guide to implementing your own Autowired annotation). I Have not yet knowingly encountered this in C++, but recently I have used this in:
- PHP via PHP-DI, which uses PSR-11.
- TypeScript using TSyringe.
- Java with Spring Framework and Burning Wave Library.
- C# where it is built-in to at least .NET core, but also works with NUnit and XUnit.
Of course, there are other types of injection from method and argument injection to property injection. I tend to find constructor injection is a marvellous place to start. The others will likely be the focus of another post later.