Entities must depend on abstractions, not on concretions. It states that the high-level module must not depend on the low-level module, but they should depend on abstractions.
Inversion of Control (IoC): Control of object creation and handling dependencies is inverted to a higher level in the application.
Dependency Injection: Dependency Injection allows high-level modules to be provided with their dependencies (low-level modules) from external sources.
Abstraction: Introducing abstractions, such as interfaces or abstract classes, helps in decoupling high-level modules from low-level modules. High-level modules depend on these abstractions instead of concrete implementations.
tightly coupled -> IoC -> DIP -> loosely couple
// Tightly Coupled Classes (Initial State) class EmailService { sendEmail(to, content) { console.log(`Sending email to ${to}: ${content}`); } } class Order { constructor() { this.emailService = new EmailService(); // Direct dependency, tightly coupled. } placeOrder(customerEmail, orderContent) { ... this.emailService.sendEmail(customerEmail, orderContent); } } const order = new Order(); order.placeOrder('customer@example.com', 'Product: XYZ, Quantity: 2');
The Order class directly creates an instance of the EmailService class, creating tight coupling between them. If we change EmailService class, we need to change Order class too.
...
Let's use Dependency Injection to implement IoC.
// Abstractions (Interfaces) // High-level modules should not depend on low-level modules. Both should depend on abstractions. interface EmailServiceInterface { sendEmail(to: string, content: string): void; } interface OrderProcessorInterface { processOrder(customerEmail: string, orderContent: string): void; } // Low-Level Module class EmailService implements IEmailService { sendEmail(to: string, content: string): void { console.log(`Sending email to ${to}: ${content}`); } } // High-Level Module with Dependency Inversion (Dependency Injection) class OrderProcessor implements IOrderProcessor { private emailService: IEmailService; constructor(emailService: IEmailService) { this.emailService = emailService; } processOrder(customerEmail: string, orderContent: string): void { ... this.emailService.sendEmail(customerEmail, orderContent); } }