xThe article was originally published at cchacin.github.io
UPDATE: Editorial changes were made to improve the readability, thanks to
Java is an object-oriented language with some functional aspects included in its core. Like any other object-oriented language, classes and objects are the foundations of any functionality that we can write and use and the relationships between the classes/objects make it possible to extend and reuse functionality. However, the way that we choose to build those relationships determine how modular, decoupled and reusable our codebase is, not only in terms of our production code but also in our test suites.
In this article, we are going to describe the concept of Dependency Injection in Java and how it helps us have a more modular and decoupled codebase which makes our lives easier, even for testing, without the need of any sophisticated container or framework.
When a class
ClassA uses any method of another class
ClassB we can say that
ClassB is a dependency of
In this example,
ClassA is calculating 10% of the value, and in order to calculate that value, it's reusing the functionality exposed by
And it can be used like this:
Now, there is a big problem with this approach:
If we needed to change/replace
ClassC has an optimized version of the
calculate() method, we will need to recompile
ClassA because we don’t have a way to change that dependency, it’s hardcoded inside of
The Dependency Injection Principle is nothing but being able to pass (
inject) the dependencies when required instead of initializing the dependencies inside of the recipient class.
Decouple the construction of your classes from the construction of your classes’ dependencies
Setter Injection (Not recommended)
With this approach, we remove the
new keyword from our
ClassA. Thus, we move the responsibility for the creation of
ClassB away from
ClassA still has a hard dependency on
ClassB but now it can be
injected from the outside:
This is definitely better than the initial approach because now we can
ClassA an instance of
ClassB or even better, a subclass of
But there is a major problem with the
Setter Injection approach:
We are hiding the
ClassB dependency on
ClassA because by reading the constructor signature we cannot identify its dependencies right away. We can write the code in this way causing a
NullPointerException that is only going to be caught on runtime:
In statically typed languages like Java is always a good thing to let the compiler help us. See
Constructor Injection (Highly recommended)
ClassA still has a hard dependency on
ClassB but now it can be
injected from the outside using the constructor:
- The functionality remains intact compared with the
- We removed the
newinitialization from the
- We still can inject a specialized subclass of
- Now the compiler is going to ask us for the dependencies that we need in compile time
Field Injection (Kids don’t try this at home)
There is a 3rd way to inject dependencies in Java, and it is called
Field Injection. The only way for field injection to work is:
- Mutating the field because it’s a non-private and non-final field
- Mutating a final/private field using reflection
This approach has the same problems exposed by the
Setter Injection approach and additionally adds complexity due to the mutation/reflection required. Unfortunately, this is a pretty common pattern when a
Dependency Injection Framework is used.
When a class
ClassAuses any method of another class
ClassBwe can say that
ClassBis a dependency of
ClassAhas a dependency in
ClassBthe latter has to be explicitly required in
Hello World example for any idea, concept, pattern, framework or library is super simple to understand and it just works fine, but when we need to implement in a real project things get more complicated and often as engineers we tend to try to solve the problem by introducing new layers to the problem instead of understanding what is the real problem.
Now that we know the advantages of the
Dependency Injection Principle using the
Constructor Injection approach, let's create a more realistic example to see some inconveniences and how can we solve it without introducing a new layer to the mix.
The Todo’s Application
Let’s design a Todo’s Application to perform CRUD operations (Create, Read, Update, Delete) to manage our todo list, an initial architecture can be like this:
TodoAppis the main class that is going to initialize our application, this can be an android app, web page or a desktop application using any framework.
TodoViewis the class that would display a view to interact with, this class is going to delegate the data-related aspects to the
TodoHttpClientand it's only responsibility is to paint/draw/render the information and get the input to perform actions against the data using the
TodoHttpClientis the class that contains a set of HTTP methods to persists
Todoobjects using a REST API.
Todois a value object that represents a todo item in our data store.
Let’s write the Java classes for our design using the
Constructor Injection approach that we just learned:
Now let’s focus our attention on the relationship between the
TodoHttpClient classes and add more details to them:
Let’s create a unit test for the
TodoView class where we test the class in isolation without instantiating any of its dependencies, in this case, the dependency is
Now that we have our test case passing, let’s analyze how our design impacts the testing approach:
- We introduced the Mockito framework in order to be able to create a fake instance of
TodoHttpClientand that adds a lot of complexity.
- We have to prepare our instance of
TodoHttpClientto fake the return of an empty list when calling the
getAll()method, now our unit test also contains implementation details about the
- Additionally, since
TodoHttpClientis a concrete class we cannot change the implementation to call a DB instead without having to change the
TodoViewclass as well, and we would need to rewrite the unit tests even when they should be isolated from this implementation detail.
One thing that we can do in order to decouple our classes is to introduce an interface since the Java language is always a good thing to rely on abstractions instead of relying on actual implementations.
Let’s put an interface between
Let’s make the
TodoHttpClient to implement that interface:
TodoView class looks like this:
What do we gain with these changes?
We are able to change the
TodoHttpClient with something like
TodoDBProvider in the
TodoApp and the application behavior would remain the same:
The test is still green which is great, but wait… nothing changed actually :(
The only changes were related to naming:
TodoProviderno value for the test.
providerno value for the test here.
- We are still relying on the mocking framework.
- We are still coupled to the interface’s name:
- We are still coupled to the method name:
If we have now an interface why are we coupled to the mocking framework in order to create a fake object that we can manually create using an anonymous class? Let’s change that:
Nice, now our design is more flexible since we can inject a different
TodoProvider implementation and we can do the same in our tests without using a mocking framework. But, we are paying a price: Verbosity, the mocking framework removes the need for implementing every single method from the interfaces.
In the next article, we are going to see how to remove that verbosity from our tests and also how can we end up with an even better design.
Stay tuned for more posts like this.
Originally published at https://cchacin.github.io.