Home > Backend Development > Python Tutorial > Breaking Down Dependency Inversion, IoC, and DI

Breaking Down Dependency Inversion, IoC, and DI

DDD
Release: 2025-01-20 16:26:09
Original
321 people have browsed it

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.


  1. 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

<code class="language-python">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)</code>
Copy after login
Copy after login

TypeScript Example

<code class="language-typescript">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);
    }
}</code>
Copy after login
Copy after login

Problems:

  1. Tight coupling: Notification directly depends on EmailService.
  2. Limited extensibility: Switching to SMSService requires modifying Notification.

With DIP

Python Example

<code class="language-python">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!")</code>
Copy after login
Copy after login

TypeScript Example

<code class="language-typescript">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!");</code>
Copy after login
Copy after login

Benefits of DIP:

  • Flexibility: Easily swap implementations.
  • Testability: Use mocks for testing.
  • Maintainability: Changes in low-level modules don't impact high-level ones.

  1. 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

<code class="language-python">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)</code>
Copy after login

TypeScript Example: Without IoC

<code class="language-typescript">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);
    }
}</code>
Copy after login

Problems Without IoC:

  1. Tight coupling.
  2. Low flexibility.
  3. Difficult testing.

Python Example: With IoC

<code class="language-python">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)</code>
Copy after login
Copy after login

TypeScript Example: With IoC

<code class="language-typescript">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);
    }
}</code>
Copy after login
Copy after login

Benefits of IoC:

  1. Loose coupling.
  2. Easy implementation switching.
  3. Improved testability.

  1. 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:

  1. Constructor Injection
  2. Setter Injection
  3. Interface Injection

Python Example: DI Framework (using injector library)

<code class="language-python">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!")</code>
Copy after login
Copy after login

TypeScript Example: DI Framework (using tsyringe library)

<code class="language-typescript">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!");</code>
Copy after login
Copy after login

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!

source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template