Design Pattern - Dependency Injection

> Code - (Programming|Computer) Language > Design Pattern

1 - About

dependency injection is a dependency resolution mechanism where components (object, bean, client) are given their (type) dependencies (service, ..) and therefore are not creating (finding, instantiating) them directly.

An injection is the passing of a Dependency (ie a service via a variable storing the client's state ) to a dependent object (a client).

We say that the control of the flow is reversed because the component does not control the construction of its dependency. This is why dependency injection is also known as Inversion of Control. For purists, dependency injection is only the component assembly part (Not the configuration and lifecycle part) of the Inversion of Control design principle.

We say that Dependency injection is a software design pattern that implements inversion of control and allows a program design to follow the dependency inversion principle and single responsibility principles.

It's a means for obtaining objects (components, bean) where they get their dependencies through:

  • their constructors arguments,
  • methods,
  • arguments to a factory method,
  • properties that are set on the object instance after it is constructed
  • or directly into fields.

Before building an object, its dependencies need to be build. And as each dependency is also itself an object, building an object is really building an object graph.

Advertising

3 - Example

For instance, you can inject a bean into a Servlet by using this annotation.

@EJB myBean ejb;

4 - Purpose

  • The purpose of Dependency Injection is to reduce coupling in your application to make it more flexible and easier to test, ensure that components are loosely coupled.
  • The core principle is to separate behavior from dependency resolution (the same than composition)
  • Objects don't have hard coded dependencies. If you need to change the implementation of a dependency, all you have to do is Inject a different type of Object.
  • Dependency injection is beneficial to most nontrivial applications.

5 - Usage

5.1 - Inject Service

Dependency injection is often used to inject services, like a logger or a translator.

5.2 - Test Mock

The object does not look up its dependencies and does not know the location or class of the dependencies. As a result, your classes become easier to test, particularly when the dependencies are on interfaces or abstract base classes, which allow for stub or mock implementations to be used in unit tests.

6 - Example

6.1 - Manually

The typical pattern is to construct the graph and then call one method on one object to send the flow of control into the object graph.

public static void main(String[] args) throws IOException {
 
    // Construction code. 
    Greeter greeter = new Greeter(System.out); // This may be many lines that connect many objects
 
    // Behavior code.
    greeter.greet(); // This is one call to one method on one object in the object graph
}
 
class Greeter {
    public void greet() {
        this.out.println("Hello world!");
    }
    public Greeter(PrintStream out) {
        this.out = out;
    }
    private PrintStream out;
}
Advertising

6.2 - Injector

Building object graphs by hand is labour intensive, error prone, and makes testing difficult. Instead, a container or dependency injector can build the object graph for you.

7 - Type

Ways to specify the injection of dependencies into components

How an object can receive a reference to an external module:

  • constructor injection: the dependencies are provided through a class constructor.
  • setter injection: the client exposes a setter method that the injector uses to inject the dependency.
  • interface injection: the dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency.
  • annotated injection Methods are marked with an annotation to indicate that they should be used for injecting dependencies

The different injector implementations:

  • factories,
  • service locators,
  • dependency injection containers - It's a library that takes care of keeping dependencies in a registry, instantiating them on demand, and making them configurable

7.1 - Without Injection

  • An example without dependency injection
public class Client {
 
    // Internal reference to the service used by this client
    private Service service;
 
    // Constructor
    Client() {
        // Specify a specific implementation in the constructor instead of using dependency injection
        service = new ServiceExample();
    }
 
    // Method within this client that uses the services
    public String greet() {
        return "Hello " + service.getName();
    }
}
Advertising

7.2 - Constructor Injection

constructor

Constructor Injection is a Dependency Injection variant where a component gets all its dependencies at construction time:

  • via its signature
  • or field/method injection

Doc

public class Client {
 
    // Internal reference to the service used by this client
    private Service service;
 
    // Constructor
    Client(Service service) {
        // The service is given through a constructor argument
        this.service = service;
    }
 
    // Method within this client that uses the services
    public String greet() {
        return "Hello " + service.getName();
    }
}

Injection Point:

  • Annotation:
    • Method: Methods are marked with an annotation to indicate that they should be used for injecting dependencies
    • Field: Fields are marked with an annotation to indicate that they should be injected into
  • Named:
    • Named Field - Fields of a certain name should be injected into
    • Named Method - If method names match other component names, injection happens

7.3 - Setter injection

Setter Injection is a Dependency Injection variant where an object gets all dependencies via setter methods.

the client exposes a setter method that the injector uses to inject the dependency.

Setter Injection is where the container or embedder hands dependencies to a component via setter methods after instantiation.

Setter Injection is idiomatic of the Spring Framework

The disadvantage of Setter Injection there is the possibility to forget to inject some of the dependencies, and the component fail later because of that unset dependency.

This method requires the client to provide a setter method for the dependency.

public class Client {
 
    // Internal reference to the service used by this client
    private Service service;
 
    // Constructor
    Client() {
    }
 
    // Setter method
    public void setService(Service service) {
        // Save the reference to the passed-in service inside this client
        this.service = service;
    }
 
    // Method within this client that uses the services
    public String greet() {
        return "Hello " + service.getName();
    }
}

Doc: http://picocontainer.com/setter-injection.html

7.4 - Interface injection

The interface injection: the dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency.

The interface define just how the injector should talk to the client when injecting dependencies.

  • Service setter interface.
public interface ServiceSetter {
    public void setService(Service service);
}
  • Client class
public class Client implements ServiceSetter {
    // Internal reference to the service used by this client.
    private Service service;
 
    // Set the service that this client is to use.
    @Override
    public void setService(Service service) {
        this.service = service;
    }
}

8 - Framework / Library

9 - Documentation / Reference