In object-oriented programming (OOP) software design, dependency injection (DI) is the process of supplying a resource that a given piece of code requires. The required resource, which is often a component of the application itself, is called a dependency.
When a client (a software component that requests something) requires other components in order to successfully carry out its intended purpose, the client must know what resources are needed, where to locate them and how to communicate with them. One way of structuring code is to embed the logic for locating resources in each client. This approach, which is tightly coupled, can be problematic, however, because if a resource should change location, the embedded code must be rewritten.
Another way to structure the code is to have clients declare their dependency on resources and allow an external piece of code to assume the responsibility for instantiating and configuring software components and their dependencies. The external piece of code, which is decoupled, can be hand coded or implemented with a special software module called a DI container or DI framework. Essentially, the container/framework provides a map of the dependencies a client might need and logic for adding new dependencies to the map.
Dependency injection can be useful when working with large applications because it relieves various code modules from the task of instantiating references to resources and allows dependencies -- even mock dependencies -- to be swapped out easily, which can make unit testing easier. By allowing the framework to do the resource creation, configuration data is centralized and updates only occur in one place. Another benefit of dependency injection is that injected resources can be customized through XML files which are outside the source code. This allows changes to be applied without having to recompile the entire codebase.
However, dependency injection can sometimes make troubleshooting difficult, as a great deal of code is pushed into an unknown location that creates resources and distributes them as needed across the application. Debugging code when all of the objects that are misbehaving are buried in a complicated third-party framework can be very frustrating and time consuming.
Miguel Castro demonstrates how dependency injection helps overcome class coupling and why that is important.