What is dependency injection and why do we need it?
According to Wikipedia, the definition and purpose of dependency injection is …
dependency injection is a technique whereby one object supplies the dependencies of another object. A “dependency” is an object that can be used, for example as a service. Instead of a client specifying which service it will use, something tells the client what service to use. The “injection” refers to the passing of a dependency (a service) into the object (a client) that would use it. The service is made part of the client’s state.[1] Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.
The intent behind dependency injection is to achieve Separation of Concerns of construction and use of objects. This can increase readability and code reuse.
Ok. I have no idea what it means. But don’t worry. we are engineers. Let’s take a look at code to understand it.
What is dependency?
Before we think about dependency injection, what does dependency mean in programming context? Please look at the code below.
There are two structs here. One is Article struct and the other one is VideoArticle struct. Article has VideoArticle, so, it is has-a relation so to say. And Article has judge() method and VideoArticle has IsPublished() method. IsPublished method is invoked in Judge method.
To put this in a diagram, it is like this.

As you can see, Article is dependent on VideoArticle. This is dependency.
What is not good about dependency?
It is not flexible and reusable.
I wouldn’t say that dependency is bad. However, it is not really flexible and reusable when there is dependency between components. For instance, you would have to modify Article struct and its method if you remove VideoArticle struct. It is not ideal situation.
It is not testable.
I will explain this in detail later. But you can’t do unit test for Judge() method because it is dependent on IsPublished(). To do unit test for Judge() method, you have to use data passed through IsPublished(). In this situation, you can’t examine where the error occurs.
OK. I got it. Then how do we use dependency injection?
Here is the example of implementation of dependency injection.
Once again, the following explanation is the purpose of dependency injection.
The intent behind dependency injection is to achieve Separation of Concerns of construction and use of objects. This can increase readability and code reuse.
So, How can we achieve Separation of Concerns? The key to achieve this is that to add interface between components. How can we add interface then? In this code, I set up ArticleInterface that defines IsPublished() method so struct that implements IsPublished() method can be used as ArticleInterface. As you can see, Article struct is no longer dependent on VideoArticle.
To put everything in a diagram, it is like this.

To make code more flexible
So, it wouldn’t affect Article struct even if you remove VideoArticle. Plus, you can add another module like BlogArticle without modifying existing Article struct.
To improve testability
To implement dependency injection, code get more flexible. What is the benefit of it? one of the benefits is that you can modify code easily and it gets more clear to examine the cause of error and so on. It is very important side of dependency injection. But it is not only that. Another good side of dependency injection is to be able to improve testability. For example, you can do mocking components.
Here is the example of test code.
I added Mock struct that implements IsPublished() method. this means mock struct can be used as VideoArticle struct. So, when this test code runs, this Mock struct’s IsPublished() method will be invoked instead of Video Article’s IsPublished() method. This is the another good part of dependency injection.
Conclusion
To wrap everything up, dependency injection makes components more flexible. Therefore you can build stronger code as followings
- It gets easier to examine the cause of error and modify the code.
- You can write unit test easily.
Reference
confirmed my code works on following environment.
go version go1.12.5 darwin/amd64
