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 software component depends upon other resources to complete its intended purpose, it needs to know which resources it needs to communicate with, where to locate them and how to communicate with them.
One way of structuring the code is to map the location of each required resource. Another way is to use dependency injections and have an external piece of code assume the responsibility of locating the resources. Typically, the external piece of code is implemented by using a framework, such as Spring for Java applications.
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.
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.
So, for example, when a user clicks a button, the event creates a new instance of a business logic layer, and one of its methods is called. Within that method, a new instance of a data access layer class is formed and one of its methods is called. In turn, this method makes a database query.
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.
These relationships are summed up in the Dependency Inversion Principle, which states "High level modules should not depend on low level modules; both should depend on abstractions. Abstractions should not depend on details. Details should depend upon abstractions."
There is more than one way to implement dependency injection. The most common way is using constructor injection, which requires that all software dependencies be provided when an object is first created. However, constructor injection makes the assumption that the entire system is using the pattern, which means the entire system must be refactored at the same time. This is difficult, risky and time-consuming.
An alternative approach to constructor injection is service location. This pattern can be implemented slowly, refactoring the application one piece at a time, as is convenient. Slow adaptation of existing systems is better than massive conversion efforts. So, when adapting an existing system to DI, service location is the best pattern to use.
There are those who criticize the service locator pattern, saying it replaces the dependencies rather than eliminating the tight coupling. However, some programmers insist that when updating an existing system, it is valuable to use the service locator during the transition, and that when the entire system has been adapted to the service locator, only a small additional step is needed to convert to constructor injection.
Inversion of Control vs. Dependency Injection
While there is some confusion and debate as to the relationship between inversion of control and dependency injection, the latter is generally considered one specific style of the former. According to Martin Fowler, chief scientist at ThoughtWorks, the confusion arises due to the rise of IoC containers. "The name is somewhat confusing (and ironic) since IoC containers are generally regarded as a competitor to Enterprise JavaBeans (EJB), yet EJB uses inversion of control just as much (if not more)," according to Fowler.
Types of DI
There are three main styles of dependency injection, according to Fowler: Constructor Injection (also known as Type 3), Setter Injection (also known as Type 2), and Interface Injection (also known as Type 1).