Dependency Injection is a fairly complex topic for beginners. It might not be necessary to know the concept but knowing it will help you make better decisions on your code.
Lets start by the definition.
Dependency Injection - Dependency Injection is the technique in which an object receives other object in which it depends on (source: Wikipedia).
Now lets try to break down the definition a bit. Lets start by object. An object is an instance of a class. For Example
// lets define a class dog
class Dog{
speak(){
console.log("wuff");
}
}
//now lets create object dog
const fluffy = new Dog();
In the above example, we have a class Dog and fluffy is the object of the class Dog. When we new up a class we create an object of that class. This is one of the ways of creating an object in Javascript (and its the common way to create an object in languages like c# and java).
Lets now see an example where 2 objects are dependent on each other.
class Pet{
whatDoesMyPetSay(){
const pet = new Dog();
pet.speak();
}
}
const fluffy = new Pet();
fluffy.whatDoesMyPetSay();
// response will be "wuff"
Here, as we see, the class Pet is dependent on class Dog. So to get what we want, we need to create an instance of Dog inside our Pet class. Now this class is not reusable as it is tied to Dog class. If someone has a cat as Pet, they wont be able to use this class. This is what is called as tightly coupled code.
Now lets change this code and try to satisfy all other pet owners with dependency Injection. But first, lets create a cat class
class Cat{
speak(){
console.log("meow");
}
}
The cat class must also implement the same method for Dependency Injection to work. In languages like C# and Java this is ensured by using an interface. But we have no such method in JavaScript, so it is up to the developer to remember it. Now lets see the new implementation of the pet class.
class Pet{
//usually we have a private variable that needs
//to be accessed only in this class
#pet;
//create a constructor that recieves the dependent
//object
constructor(pet){
this.#pet = pet;
}
whatDoesMyPetSay(){
//as long as pet class implements speak method we are fine
this.#pet.speak();
}
}
//what does fluffy the dog say?
const fluffy = new Pet(new Dog());
fluffy.whatDoesMyPetSay();
//The response will be "wuff"
//what does milo the cat say?
const milo = new Pet(new Cat());
milo.whatDoesMyPetSay();
//The response will be "meow"
Now, we have removed the dependency from inside the pet class and have given it to the caller of the class. This promotes the reusability of the pet class. This is a very simple example and the purpose is to only understand dependency injection and not to implement it. In real world, the dependency is abstracted even from the caller and given to a new object, which is usually called an Injector.
Why not to use Dependency Injection in JavaScript
If you have read till here I hope you are clearer with the concept of dependency injection. Now lets see some reasons we might not want to use dependency injection
- Unlike purely class based languages like c# and java, JavaScript provides us lots of flexibility when it comes to grouping functionalities. Many times, you won't even need to use a class and just using a function might be enough. In those cases, trying to implement Dependency injection will only create unnecessary complexities.
- JavaScript also happens to be dynamic in nature. You can overwrite and use any function implementation in JavaScript. So we should make use of these rather then dependency injection for unit testing our code.
At the end of the day, as a developer, we should realize that there is no one solution that fits all problems. I hope with this article, you would be able to take a better decision on your solutions.
Top comments (17)
What you describe is not dependency injection, but dependency inversion. You made it so the class Pet does not automatically create a dependency, but rather takes it as a parameter. I think an important bit that you left out is the fact that the instance of the Dog class(fluffy) can be reused in other classes. Good point to specify a purpose for dependency inversion is to create more instances of the dependant class with different dependencies. The bit about the injector is right, dependency injection is a pattern where you don't even need to create any of your services / components / dependencies, but rather let a framework create them for you and figure out the order of the creation and injection of the dependencies. I wish you detailed more on that part.
My aim was to make people understand the concept of loose coupling through dependency injection. This is a beginner focused post and don't want to confuse people with information they might not need. Thanks for reading.
Without being an expert I think that dependency injection is one form of practical application of the dependency inversion.
right, agree
I agree with the Author. Js is highly dynamic and powerful so I think it does not really need any kind of IOC. If I pick up the example I will probably end up with something like this:
function whatDoesMyPetSay < T extends { speak: () => string; }>(pet: T): string {
pet.speak();
}
🙂
Yes, I agree, You can do it in many ways in JavaScript.
many thanks. I've read about
"Dependency inversion principle"
and I know it clearly after reading your article.Even though JS doesn't have interface, this is a good, clear and simple example.
in JS files, I usually use
export and import
some functions into a module, so is it the way could replace DI?Yes, you dont really need to create class objects to use funtions in js. You can directly use functions in most of the case. Glad you liked it.
JavaScript also happens to be dynamic in nature.
Great
Thanks for reading.
Good read
Thanks for reading
Dependency injection is passing dependencies as parameters, js supports higher-order functions, so why just don't use partial application and pass functions as dependencies to other functions?
Yes, that will indeed be the most optimal way in javascript or any other functional programming language. But DI is very Object oriented concept. If you are working with Class Objects you might be need to use DI. Its all based on situation of use.
Very nice and easy explanation of not so easy concept. Thanks 👍🏻
Thanks, glad you liked it :)