Dependency Injection is a software design pattern used to implement Inversion of Control to avoid dependencies in your code.
Now, that’s a pretty standard definition. But before we discuss the concept of Dependency Injection(DI) deeply, we need to understand two key words “Dependency” and “Inversion of Control”.
Dependency: In programming, Objects/Services often depend on each other to complete their functionality. For example, a class responsible for creating a new user (say signUp) need help of another class (say passwordChecker) that checks whether the password is in valid format or not. In this case, it is said that signUp class has a dependency in passwordChecker.
Inversion of Control: Inversion of Control (IOC) is a one of the five SOLID principles used in OOD. It states that any high level module should not depend on the low level module to complete it’s functionality.
But in our example used above, signUp class is dependent the passwordChecker to complete its functionality. This is where DI comes into picture.
Now let’s see how dependency Injection solves the above problem. In dependency injection, the high level modules don’t create the low level modules. It happens in an Inverted way. The Injectors create the low level modules first and then injects those objects to whatever the high level modules that require them.
In this scenario, the passwordChecker is created first and then it is injected into the signUp class. Let’s see it in action in Angular.
Dependency Injection in Angular.
Angular implements the DI using the services. The service that needs to be injected into other class must be decorated with the decorator
@Injectable . This tells the angular that the service needs to be created first to be injected into other components/services. The attribute
providedIn: 'root' tells the injector that the service is available throughout the application.
The service is then injected into the dependent classes by passing the service as a parameter into the constructor as show below.
As you can see in the code above, the class or the constructor is not making any calls to the service. It is provided directly by dependency injectors and it simply makes use of it. This way, the class is not dependent on the passwordChecker class to be created.
Uses of DI:
- Extending the functionality is easier. You just need to create a new service and inject it. You don’t have to modify the class.
- Helps in Unit testing. Mocking services is easier.
- Loose Coupling. Helps in reducing the dependencies between the components.
But it also comes with a couple of issues. Since DI happens at the run time, all the errors and issues cannot be caught during the compilation. Since the DI metadata is generated at the runtime, advanced IDE features like Refactoring, Finding references.
Hope you enjoyed the reading.
Please feel free to connect with me on karanam raghuvardhan (@urstruly_raghu) / Twitter.