Dependency Injection

Dependency Injection is a software technique where we can create objects that depend on other objects. The aim is to separate the construction of objects from their use. DI helps with the interaction between classes but at the same time keeps the classes independent. Meaning that an object that wants to use a service does not know how to construct that service. Instead, the receiving client is given its dependencies by external code, which it is not aware of:

  • A service is a class that can because (The dependency)

  • A client is a class that uses the dependency

  • The injector passes the service to the client

DI also solves the following problems: Makes a class independent from the creation of the objects it depends on; Makes the application and its objects support different configurations; The behavior of a piece of code can be changed without editing it directly.

Because the classes are loosely coupled, it means that the creation of client dependencies is separated from the client’s behavior. By removing the client’s knowledge of how its dependencies are implemented, programs become more reusable, testable, and maintainable.

Below is a simplified example of DI. In this code, we don’t have DI because the client Client class is creating an object of the service class. The dependency is hardcoded because the client directly constructs and controls which service is used

public class Client {
  private ExampleService service;
  public Client() {
    service = new ExampleServce();
  }
}

The service object can also be passed as a parameter in the class constructor. This is the most common form of dependency injection. After all, it ensures that the client is always in a valid state because it cannot be instantiated without its necessary dependencies.

public class Client {
  private Service service;
  public Client(Service service) {
    this.service = service;
  }
}

Constructor injection: Dependencies are passed to a client class constructor. An example of the constructor injection was already shown in the preceding example code.

Setter injection: Dependencies are provided through setters. This means that clients can allow injectors to manipulate their dependencies at any time. Even though this offers flexibility, it also makes it difficult to make sure that all dependencies are injected and valid before the client is used The following example code shows an example of the setter injection:

public class Client {
  private Service service;
  public void setService(Service service) {
    this.service = service;
  }
}