Maison >développement back-end >Tutoriel Python >Décomposer l'inversion de dépendance, l'IoC et la DI

Décomposer l'inversion de dépendance, l'IoC et la DI

DDD
DDDoriginal
2025-01-20 16:26:09282parcourir

Breaking Down Dependency Inversion, IoC, and DI

L'exploration du système d'injection de dépendances de NestJS a déclenché une plongée plus approfondie dans l'inversion de dépendance, l'inversion de contrôle et l'injection de dépendance. Ces concepts, bien qu’apparemment similaires, offrent des solutions distinctes à différents problèmes. Cette explication sert de rappel personnel et, espérons-le, de guide utile pour ceux qui sont aux prises avec ces termes.


  1. Principe d'inversion de dépendance (DIP)

Définition : Les modules de haut niveau ne doivent pas dépendre des modules de bas niveau ; les deux devraient dépendre d’abstractions. Les abstractions ne devraient pas dépendre de détails ; les détails devraient dépendre des abstractions.

Ce que cela signifie :

Dans les logiciels, les modules de haut niveau encapsulent la logique métier de base, tandis que les modules de bas niveau gèrent des implémentations spécifiques (bases de données, API, etc.). Sans DIP, les modules de haut niveau s'appuient directement sur ceux de bas niveau, créant un couplage étroit qui entrave la flexibilité, complique les tests et la maintenance et rend difficile le remplacement ou l'extension des détails de bas niveau.

DIP inverse cette relation. Au lieu d'un contrôle direct, les modules de haut niveau et de bas niveau dépendent d'une abstraction partagée (interface ou classe abstraite).


Sans DIP

Exemple Python

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

Exemple de TypeScript

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

Problèmes :

  1. Couplage serré : Notification dépend directement de EmailService.
  2. Extensibilité limitée : le passage à SMSService nécessite de modifier Notification.

Avec DIP

Exemple Python

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

Exemple de TypeScript

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

Avantages du DIP :

  • Flexibilité : échangez facilement les implémentations.
  • Testabilité : utilisez des simulations pour les tests.
  • Maintenabilité : les modifications apportées aux modules de bas niveau n'ont pas d'impact sur ceux de haut niveau.

  1. Inversion de contrôle (IoC)

IoC est un principe de conception selon lequel le contrôle des dépendances est transféré vers un système externe (framework) au lieu d'être géré au sein de la classe. Traditionnellement, une classe crée et gère ses dépendances. IoC inverse cela : une entité externe injecte des dépendances.


Exemple Python : sans 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>

Exemple TypeScript : sans 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>

Problèmes sans IoC :

  1. Couplage serré.
  2. Faible flexibilité.
  3. Tests difficiles.

Exemple Python : avec 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>

Exemple TypeScript : avec 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>

Avantages de l'IoC :

  1. Couplage lâche.
  2. Changement de mise en œuvre facile.
  3. Testabilité améliorée.

  1. Injection de dépendance (DI)

DI est une technique où un objet reçoit ses dépendances d'une source externe. C'est une implémentation pratique d'IoC, injectant des dépendances via :

  1. Injection de constructeur
  2. Injection de Setter
  3. Injection d'interface

Exemple Python : DI Framework (utilisant la bibliothèque injector)

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

Exemple TypeScript : DI Framework (utilisant la bibliothèque tsyringe)

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

Avantages de DI :

  • Tests simplifiés.
  • Évolutivité améliorée.
  • Maintenabilité améliorée.

Cette explication détaillée clarifie les relations et les distinctions entre DIP, IoC et DI, en mettant l'accent sur leurs contributions individuelles à la création de logiciels robustes et maintenables.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Article précédent:Validation AWS CDK en PythonArticle suivant:Validation AWS CDK en Python