What is Dependency Injection (DI) ?

What is Dependency Injection (DI) ?

During my Angular2 learning journey, I encountered a new word Dependency Injection, which I have no idea at first. And after reviewed the Wiki definition, I felt even more confused! What’s the magic really is and what does it really do? Fortunately, I finally figure it out based on my hours research. Below is my personal understanding about Dependency Injection (Example language is TypeScript).

See, let’s create a User class like below.

1
2
3
4
5
6
7
8
9
export class User {
private login: Login;
private contact: Contact;
constructor() {
this.login = new Login('test', '12345678'); // username & password
this.contact = new Contact('test@gmail.com', 'Mars'); // email & address
}
}

When I first learning Object-Oriented programming, I probably will write the above code and think it’s pretty cool. But actually, this is a bad example. What about I wanna change the usernam and password? And also the input is dynamic, not hard code. OK. You may change the code like below.

1
2
3
4
5
6
7
8
9
export class User {
private login: Login;
private contact: Contact;
constructor(username: string, password: string, email: string, address: string) {
this.login = new Login(username, password);
this.contact = new Contact(email, address);
}
}

I’m one hundred percent sure I will write above code even now. And I’m sure there are many other programmers as well.

Let us consider one scenario. See if we want to add more fields of Contact class, like phone, country, etc. In that case, we need to change the User class as well. To add corresponding arguments into constructor and assign to new Contact. You may say this is not a big change. But honestly speaking, anytime of the Child constructor changed, the Parent constructor has to be changed. And also considering unit test, the above code is hard to mock the Login and Contact effectively. As a result, here is the magic come out.

1
2
3
4
5
6
7
8
9
export class User {
private login: Login;
private contact: Contact;
constructor(login: Login, contact: Contact) {
this.login = login;
this.contact = contact;
}
}

Now, User class does not need to know anything about Login and Contact. So if Child constructor changed, no need to change Parent constructor. Awesome! In Typescript you can be written in shorthand like.

1
2
3
4
export class User {
constructor(private login: Login, private contact: Contact) {
}
}

Cool! Isn’t it? There are just four lines. But you may ask how does those magic happened? How can the User to manage the Login and Contact? Here is the dependency injection framework come out. The above code is just a model of dependency injection. The back-end logic is managed by DI framework, and more specifically it’s the Injector oject, like.

1
2
const injector = new Injector([User, Login, Contact]);
const user = injector.get(User);

All of the complex work is automatically finished by DI framework. And almost front-end framework support DI, you can just benefited from it and make your code cleaner and more testable.