What is Dependency Injection? (DI) is a very simple concept that aims to decouple components of your software and ease their integration and testing. It does so by asking for their sub-components instead of creating them. Dependency injection During this article, we will also mention (IoC), which is commonly used along with dependency injection. This pattern aims to avoid asking for implementations but rather interfaces while injecting dependencies. inversion of control This article will use a simple example in Java to present dependency injection but aims towards a technology-agnostic explanation of the concept and its advantages. Moreover, even if it is an object-oriented design pattern, you can still adapt the behaviour in many programming languages. Let’s clarify all this using an example! We will present a weather service that shows an intelligible representation of the weather. In the current implementation, we rely solely on a thermometer . Let’s start without dependency injection. As you can see on the diagram, the is relying on a , which can be configured with a . Not using dependency injection will result in a code creating a new instance of in the service, and a configuring the to use: WeatherService Thermometer TemperatureUnit Thermometer Thermometer TemperatureUnit { TemperatureUnit unit; { .unit = TemperatureUnit.CELSIUS; } } { Thermometer thermometer; { .thermometer = Thermometer(); } } public class Thermometer private final public Thermometer () this public class WeatherService implements WeatherContract private final // This constructor is not using dependency injection public WeatherService () this new Now let’s imagine that we want to use a configured to use Fahrenheit degrees instead of Celsius. For this, we add a parameter to switch between both units. Thermometer { (useCelsius) { .unit = TemperatureUnit.CELSIUS; } { .unit = TemperatureUnit.FAHRENHEIT; } } public Thermometer ( useCelsius) boolean if this else this One can also argue that the user of our program won’t always have access to an actual thermometer on their device, thus you may want to be able to fall back to another implementation in this case. For instance, an API sending the current temperature in your area. Integrating multiple implementations inside the service could be done as shown below. { (useRealDevice) { .thermometer = Thermometer(useCelsius); } { .thermometer = ThermometerWebService(useCelsius, apiKey); } } public WeatherService ( useRealDevice, useCelsius, String apiKey) boolean boolean if this new else this new As a result, initializing the service can be done as follows: { WeatherContract weather = WeatherService( , , ); } public static void main (String[] args) // Not using dependency injection new true true null Even if it is easy to use, our current version of the is not evolutive. If we take a closer look at its constructor, we can see multiple design flaws that will haunt us in the long run: WeatherService The constructor is choosing its . Adding a new type of Thermometer would require some parameter tricks to guess the implementation to use. Thermometer The constructor is managing the constructor parameters. Adding the forced us to add a new parameter to it, even if unrelated to the . Thermometer ThermometerWebService apiKey WeatherService As a result, any change to any implementation may require changes on the constructors. This behaviour is unwanted and breaks the principle. Thermometer WeatherService Separation of Concerns Will dependency injection improve my project? Dependency injection, associated with inversion of control, is a good way to cover this use case. It allows you to choose which kind of thermometer you want in your program depending on the situation. The following diagram gives a quick overview of our new architecture: The is represented in this diagram by the fact that our implementation is linked to rather than any of its implementations. That’s nothing more than this. inversion of control WeatherService ThermometerContract As for , will now take a in its constructor, requiring the block using the service to build an instance filling this contract: dependency injection WeatherService ThermometerContract { ThermometerContract thermometer; { .thermometer = thermometer; } } public class WeatherService implements WeatherContract // We now use the Interface private final // New constructor using dependency injection public WeatherService (ThermometerContract thermometer) this As a result, the initialization of a for both constructors will look like the following: WeatherService { TemperatureUnit celsius = TemperatureUnit.CELSIUS; ThermometerContract thermometer = Thermometer(celsius); WeatherContract weather = WeatherService(thermometer); } public static void main (String[] args) // Using dependency injection new new Now, our can be fully configured by an external part of the software. More important so, the doesn’t need to know any of the available implementations of , thus decoupling your software packages. ThermometerContract WeatherService ThermometerContract This could seem like nothing important, but this simple switch of responsibility is critical leverage for multiple aspects of software design. It enables you to control the instance creation from your software entry point by chaining dependencies. You won’t have to take care of the instantiation until it is necessary. This behaviour could be compared to raised exceptions, that are ignored until taken care of in a significant context. That’s all there is to dependency injection? It is important to know that even if you can find libraries that help you manage your dependency injection, it is not always necessary to use them. Those libraries tend to cover a lot of cases thus be offputting to developers not comfortable with the pattern in the first place. In reality, they simply ease the instantiation of complex dependency trees and are not required at all. The following section is an example of injecting our service using Guice, a dependency injection framework for Java made by Google. The concept is to reference bindings of every component you can inject in your program, so that the library can generate a class of any type, automatically. Let’s consider that we have two implementations with the following constructors: { ThermometerContract thermometer; { .thermometer = thermometer; } } { TemperatureUnit unit; { .unit = unit; } } public class WeatherService implements WeatherContract private final @Inject public WeatherService (ThermometerContract thermometer) this public class Thermometer implements ThermometerContract private final @Inject TemperatureUnit unit) public Thermometer (@Named(WeatherModule.TEMPERATURE_UNIT) this The should be configured to bind all needed interfaces to a given implementation. It should also be able to inject any object without a specific interface, such as the enumerate The injection will then be bound to a specific name, “ in this case injection module TemperatureUnit . temp_unit” . { String TEMPERATURE_UNIT = ; { bind(TemperatureUnit.class) .annotatedWith(Names.named(TEMPERATURE_UNIT)) .toInstance(TemperatureUnit.CELSIUS); bind(ThermometerContract.class).to(Thermometer.class); bind(WeatherContract.class).to(WeatherService.class); } } public class WeatherModule extends AbstractModule public static final "temp_unit" @Override protected void configure () // Named input configuration bindings // Interface - Implementation bindings Ultimately, the module can be used as follow, here instantiating a . WeatherContract { Injector injector = Guice.createInjector( WeatherModule()); WeatherContract weather = injector.getInstance(WeatherContract.class); } public static void main (String[] args) // Creating the injection module configured above. new // We ask for the injection of a WeatherContract, // which will create an instance of ThermometerContract // with the named TemperatureUnit under the hood. Such modules usually provide a good power of customization to the injected elements, thus we can consider configuring the injection depending on the available implementations. As a result, using a library is not required when integrating dependency injection. However, this could save a lot of time and cumbersome code in big projects. Show me some tests! As a side effect of decoupling your code, the dependency injection pattern is a real asset to improve unit testability of each component. This section contains an example of unit tests for our . WeatherService As said above, making asking for a enables us to use any implementation we want. Hence, we can send a in the constructor, then control its behaviour from the outside. WeatherService ThermometerContract mock { ThermometerContract thermometer = Mockito.mock(ThermometerContract.class); Mockito.doReturn(TemperatureUnit.CELSIUS).when(thermometer).getUnit(); WeatherContract weather = WeatherService(thermometer); Mockito.doReturn(- ).when(thermometer).getTemperature(); assertEquals( TemperatureStatus.COLD, weather.getTemperatureStatus() ); Mockito.doReturn( ).when(thermometer).getTemperature(); assertEquals( TemperatureStatus.MODERATE, weather.getTemperatureStatus() ); } public void testTemperatureStatus () new 50f 10f As you can see, we can then control our thermometer without a struggle from outside our tested class. Conclusion is a way of thinking about your code architecture and can be simple to implement by yourself. In bigger projects, integrating a dependency injection framework can save you a lot of time in the long run. Dependency injection provides multiple non-negligible advantages such as: Dependency injection : use the contracts and ignore implementation specificities. Code decoupling : Unit tests almost become a pleasure to write. Enhanced testability : you can more easily swap injected instances. Configurability You can find the full code example in my design tutorials repository on GitHub. This article was first seen at: https://aveuiller.github.io/about_design_patterns-dependency_injection.html