Breaking Down Dependency Inversion, IoC, and DI
Exploring NestJS's dependency injection system sparked a deeper dive into Dependency Inversion, Inversion of Control, and Dependency Injection. These concepts, while seemingly similar, offer distinct solutions to different problems. This explanation serves as a personal refresher, and hopefully, a helpful guide for others grappling with these terms.
-
Dependency Inversion Principle (DIP)
Definition: High-level modules shouldn't depend on low-level modules; both should depend on abstractions. Abstractions shouldn't depend on details; details should depend on abstractions.
What This Means:
In software, high-level modules encapsulate core business logic, while low-level modules handle specific implementations (databases, APIs, etc.). Without DIP, high-level modules directly rely on low-level ones, creating tight coupling that hinders flexibility, complicates testing and maintenance, and makes replacing or extending low-level details difficult.
DIP reverses this relationship. Instead of direct control, both high-level and low-level modules depend on a shared abstraction (interface or abstract class).
Without DIP
Python Example
class EmailService: def send_email(self, message): print(f"Sending email: {message}") class Notification: def __init__(self): self.email_service = EmailService() def notify(self, message): self.email_service.send_email(message)
TypeScript Example
class EmailService { sendEmail(message: string): void { console.log(`Sending email: ${message}`); } } class Notification { private emailService: EmailService; constructor() { this.emailService = new EmailService(); } notify(message: string): void { this.emailService.sendEmail(message); } }
Problems:
- Tight coupling:
Notification
directly depends onEmailService
. - Limited extensibility: Switching to
SMSService
requires modifyingNotification
.
With DIP
Python Example
from abc import ABC, abstractmethod class MessageService(ABC): @abstractmethod def send_message(self, message): pass class EmailService(MessageService): def send_message(self, message): print(f"Sending email: {message}") class Notification: def __init__(self, message_service: MessageService): self.message_service = message_service def notify(self, message): self.message_service.send_message(message) # Usage email_service = EmailService() notification = Notification(email_service) notification.notify("Hello, Dependency Inversion!")
TypeScript Example
interface MessageService { sendMessage(message: string): void; } class EmailService implements MessageService { sendMessage(message: string): void { console.log(`Sending email: ${message}`); } } class Notification { private messageService: MessageService; constructor(messageService: MessageService) { this.messageService = messageService; } notify(message: string): void { this.messageService.sendMessage(message); } } // Usage const emailService = new EmailService(); const notification = new Notification(emailService); notification.notify("Hello, Dependency Inversion!");
Benefits of DIP:
- Flexibility: Easily swap implementations.
- Testability: Use mocks for testing.
- Maintainability: Changes in low-level modules don't impact high-level ones.
-
Inversion of Control (IoC)
IoC is a design principle where dependency control shifts to an external system (framework) instead of being managed within the class. Traditionally, a class creates and manages its dependencies. IoC reverses this—an external entity injects dependencies.
Python Example: Without IoC
class SMSService: def send_message(self, message): print(f"Sending SMS: {message}") class Notification: def __init__(self): self.sms_service = SMSService() # Dependency created internally def notify(self, message): self.sms_service.send_message(message)
TypeScript Example: Without IoC
class SMSService { sendMessage(message: string): void { console.log(`Sending SMS: ${message}`); } } class Notification { private smsService: SMSService; constructor() { this.smsService = new SMSService(); // Dependency created internally } notify(message: string): void { this.smsService.sendMessage(message); } }
Problems Without IoC:
- Tight coupling.
- Low flexibility.
- Difficult testing.
Python Example: With IoC
class EmailService: def send_email(self, message): print(f"Sending email: {message}") class Notification: def __init__(self): self.email_service = EmailService() def notify(self, message): self.email_service.send_email(message)
TypeScript Example: With IoC
class EmailService { sendEmail(message: string): void { console.log(`Sending email: ${message}`); } } class Notification { private emailService: EmailService; constructor() { this.emailService = new EmailService(); } notify(message: string): void { this.emailService.sendEmail(message); } }
Benefits of IoC:
- Loose coupling.
- Easy implementation switching.
- Improved testability.
-
Dependency Injection (DI)
DI is a technique where an object receives its dependencies from an external source. It's a practical implementation of IoC, injecting dependencies via:
- Constructor Injection
- Setter Injection
- Interface Injection
Python Example: DI Framework (using injector
library)
from abc import ABC, abstractmethod class MessageService(ABC): @abstractmethod def send_message(self, message): pass class EmailService(MessageService): def send_message(self, message): print(f"Sending email: {message}") class Notification: def __init__(self, message_service: MessageService): self.message_service = message_service def notify(self, message): self.message_service.send_message(message) # Usage email_service = EmailService() notification = Notification(email_service) notification.notify("Hello, Dependency Inversion!")
TypeScript Example: DI Framework (using tsyringe
library)
interface MessageService { sendMessage(message: string): void; } class EmailService implements MessageService { sendMessage(message: string): void { console.log(`Sending email: ${message}`); } } class Notification { private messageService: MessageService; constructor(messageService: MessageService) { this.messageService = messageService; } notify(message: string): void { this.messageService.sendMessage(message); } } // Usage const emailService = new EmailService(); const notification = new Notification(emailService); notification.notify("Hello, Dependency Inversion!");
Benefits of DI:
- Simplified testing.
- Improved scalability.
- Enhanced maintainability.
This detailed explanation clarifies the relationships and distinctions between DIP, IoC, and DI, emphasizing their individual contributions to building robust and maintainable software.
The above is the detailed content of Breaking Down Dependency Inversion, IoC, and DI. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

Solution to permission issues when viewing Python version in Linux terminal When you try to view Python version in Linux terminal, enter python...

How to avoid being detected when using FiddlerEverywhere for man-in-the-middle readings When you use FiddlerEverywhere...

When using Python's pandas library, how to copy whole columns between two DataFrames with different structures is a common problem. Suppose we have two Dats...

How to teach computer novice programming basics within 10 hours? If you only have 10 hours to teach computer novice some programming knowledge, what would you choose to teach...

How does Uvicorn continuously listen for HTTP requests? Uvicorn is a lightweight web server based on ASGI. One of its core functions is to listen for HTTP requests and proceed...

Fastapi ...

Using python in Linux terminal...

Understanding the anti-crawling strategy of Investing.com Many people often try to crawl news data from Investing.com (https://cn.investing.com/news/latest-news)...
