Decouple Your Code With Dependency Injection
By: Ben Weidig
Tags: Java, Programming, Software Engineering, Software Development, Development

Dependency Injection

James Shore, the author of The Art of Agile Development, put it quite nicely:

“Dependency injection is a 25-dollar term for a 5-cent concept.”

The concept is actually really simple: Giving a component all the things it needs to do its job.

In general, it means decoupling components by providing their dependencies from the outside, instead of creating them directly, which would create adhesion.

There are different ways how we can provide an instance with its necessary dependencies:

Constructor injection

Property injection

Method injection

Constructor injection

Constructor, or initializer-based dependency injection, means providing all required dependencies during the initialization of an instance, as constructor arguments:

Thanks to this simple change, we can offset most of the initial disadvantages:

Easily replaceable: DbManager and Calculator are no longer bound to the concrete implementations, and are now mockable for unit-testing.

and are no longer bound to the concrete implementations, and are now mockable for unit-testing. Already initialized and “ready-to-go”: We don’t need to worry about any sub-dependencies required by our dependencies (e.g., database filename, significant digits), or that they might crash during initialization.

Mandatory requirements: The caller knows exactly what’s needed to create a DataProcessor .

. Immutability: Dependencies are still final.

Even though constructor injection is the preferred way of many DI frameworks, it has its obvious disadvantages, too. The most significant one is that all dependencies must be provided at initialization.

Sometimes, we don’t initialize a component ourselves or we aren’t able to provide all dependencies at that point. Or we need to use another constructor. And once the dependencies are set, they can’t be changed.

But we can mitigate these problems by using one of the other injection types.

Property injection

Sometimes we don’t have access to the actual initialization of type, and only have an already initialized instance. Or the needed dependency is not explicitly known at initialization as it would be later on.

In these cases, instead of relying on a constructor, we can use property injection:

No constructor is needed anymore, we can provide the dependencies at any time after initialization. But this way of injection also comes with drawbacks: Mutability.

Our DataProcessor is no longer guaranteed to be “ready-to-go” after initialization. Being able to change the dependencies at will might give us more flexibility, but also the disadvantage of more runtime-checks.

We now have to deal with the possibility of a NullPointerException when accessing the dependencies.

Method injection

Even though we decoupled the dependencies with constructor injection and/or property injection, by doing so, we still only have a single choice. What if we need another Calculator in some situations?

We don’t want to add additional properties or constructor arguments for a second Calculator , because there might be a third one needed in the future. And changing the property every time before we call calc(...) isn't feasible either, and will most likely lead to bugs using the wrong one.

A better way is to parameterize the method call itself with its dependency:

Now the caller of calc(...) is responsible for providing an appropriate Calculator instance, and DataProcessor is completely decoupled from it.

Even more flexibility can be gained by mixing different types of injection, and providing a default Calculator :