Skip to main content

Dependency Injection

StructuralDecouplingDependency managementInversion of controlAbout 2 min

Also known as

  • Inversion of Control (IoC)
  • Dependency Inversion

Intent

Dependency Injection is a software design pattern in which one or more dependencies (or services) are injected, or passed by reference, into a dependent object (or client) and are made part of the client's state. The pattern separates the creation of a client's dependencies from its own behavior, which allows program designs to be loosely coupled and to follow the Inversion of Controlopen in new window and Single Responsibilityopen in new window principles.

Explanation

Real world example

The old wizard likes to fill his pipe and smoke tobacco once in a while. However, he doesn't want to depend on a single tobacco brand only but likes to be able to enjoy them all interchangeably.

In plain words

Dependency Injection separates creation of client's dependencies from its own behavior.

Wikipedia says

In software engineering, dependency injection is a technique in which an object receives other objects that it depends on. These other objects are called dependencies.

Programmatic Example

Let's first introduce the Tobacco interface and the concrete brands.


@Slf4j
public abstract class Tobacco {

    public void smoke(Wizard wizard) {
        LOGGER.info("{} smoking {}", wizard.getClass().getSimpleName(),
                this.getClass().getSimpleName());
    }
}

public class SecondBreakfastTobacco extends Tobacco {
}

public class RivendellTobacco extends Tobacco {
}

public class OldTobyTobacco extends Tobacco {
}

Next here's the Wizard class hierarchy.

public interface Wizard {

    void smoke();
}

public class AdvancedWizard implements Wizard {

    private final Tobacco tobacco;

    public AdvancedWizard(Tobacco tobacco) {
        this.tobacco = tobacco;
    }

    @Override
    public void smoke() {
        tobacco.smoke(this);
    }
}

And lastly we can show how easy it is to give the old wizard any brand of tobacco.

var advancedWizard=new AdvancedWizard(new SecondBreakfastTobacco());
advancedWizard.smoke();

Class diagram

Dependency Injection
Dependency Injection

Applicability

  • When aiming to reduce the coupling between classes and increase the modularity of the application.
  • In scenarios where the object creation process is complex or should be separated from the class usage.
  • In applications requiring easier unit testing by allowing dependencies to be mocked or stubbed.
  • Within frameworks or libraries that manage object lifecycles and dependencies, such as Spring or Jakarta EE (formerly Java EE).

Known Uses

  • Frameworks like Spring, Jakarta EE, and Google Guice use DI extensively to manage component lifecycles and dependencies.
  • Desktop and web applications that require flexible architecture with easily interchangeable components.

Consequences

Benefits:

  • Enhances modularity and separation of concerns.
  • Simplifies unit testing by allowing for easy mocking of dependencies.
  • Increases flexibility and maintainability by promoting loose coupling.

Trade-offs:

  • Can introduce complexity in the configuration, especially in large projects.
  • Might increase the learning curve for developers unfamiliar with DI patterns or frameworks.
  • Requires careful management of object lifecycles and scopes.

Credits