In this article, we will implement Inversion of Control (IoC ) using a technique called Dependency Injection (DI) which allows injecting the dependent objects into the class rather than having the class generate them.
According to the design principle "Inversion of Control", an external entity is given control over a program's flow instead of the main programme. Put another way, we cede control to someone else in exchange for their ability to give us the dependencies, tools and capabilities that we require.
Dependency Injection examples
Example without DI
Without Dependency Injection we will have the concrete Logger implementation and Services closely connected and depends on each other:
import { ConsoleLogger } from "./console.logger";
export class Service {
private logger: any;
constructor() {
this.logger = new ConsoleLogger();
}
// ...
}
There is a hard-coded dependency of the ConsoleLogger implementation making it not flexible and tightly couple.
Example of Constructor Injection
In this example the dependency is injected through the class constructor declaring the dependency as an interface type:
import { ILogger } from "./logger.interface";
export class Service {
// Constructor Injection
constructor(private logger: ILogger) { }
// ...
}
Since the Service class in our example receives the Logger instance through its constructor, changing or mocking the dependence in various scenarios is made simpler:
import { ConsoleLogger } from "./console.logger";
class Main {
constructor() {
// Manually arrange the concrete logger implementation
const logger = new ConsoleLogger();
// Pass the console logger instance to the service constructor
const service = new Service(logger);
// ...
}
}
Advantages
Loose coupling between classes and their dependencies
Systems has reusable, testable and maintainable components
Easy to test: Easier to mock the components and provided it as a dependencies.
Refactor or replace components: Just change the code implementation without touch the dependency's consumers.
Pluggable components: Easy to swap between libraries or concrete implementations.
Disadvantages
Makes the code hard to understand due to the higher DI learning curve.
Increases the project size: Increasing the number of classes or components since the DI promotes a clearly separation of responsibilities.
In conclusion, IoC is a design principle that grants external entities control over a program's flow, while DI is a method for putting IoC into practice by injecting dependencies into a class.
See you in the next article.