Maison >développement back-end >Tutoriel Python >Principes SOLID - Expliqués à l'aide d'exemples réels en Python

Principes SOLID - Expliqués à l'aide d'exemples réels en Python

王林
王林original
2024-09-03 11:39:29661parcourir

SOLID Principles - Explained Using Real World Examples in Python

Principes SOLID (Crédit image : FreeCodeCamp)

SOLID est un acronyme qui représente cinq principes de conception qui aident les développeurs à créer des logiciels plus maintenables, compréhensibles et flexibles. Passons en revue chacun d'entre eux avec un exemple pertinent.

1. S - Principe de responsabilité unique (SRP)

Définition : Une classe ne devrait avoir qu'une seule raison de changer, ce qui signifie qu'elle ne devrait avoir qu'un seul travail ou responsabilité.

Explication : Imaginez que vous disposez d'un outil qui combine deux tâches différentes, comme l'envoi d'e-mails et le traitement des paiements. Si les deux tâches sont gérées par une seule classe, les modifications apportées à la fonctionnalité de courrier électronique peuvent interrompre la fonctionnalité de paiement. En gardant ces responsabilités séparées, vous minimisez le risque que des changements dans une partie en affectent une autre.

Exemple :

class EmailSender:
    def send_email(self, recipient, subject, body):
        # Code to send an email
        print(f"Sending email to {recipient} with subject '{subject}'")

class PaymentProcessor:
    def process_payment(self, amount):
        # Code to process payment
        print(f"Processing payment of amount {amount}")

# Usage
email_sender = EmailSender()
email_sender.send_email("user@example.com", "Hello!", "Welcome to our service!")

payment_processor = PaymentProcessor()
payment_processor.process_payment(100)

Dans cet exemple, EmailSender est uniquement responsable de l'envoi des e-mails et PaymentProcessor est uniquement responsable du traitement des paiements. Ils ont chacun une responsabilité unique, ce qui rend le code plus facile à maintenir et à étendre.

2. O - Principe ouvert/fermé (OCP)

Définition : Les entités logicielles (comme les classes, les modules, les fonctions, etc.) doivent être ouvertes pour extension mais fermées pour modification.

Explication : Cela signifie que vous devriez pouvoir ajouter de nouvelles fonctionnalités ou comportements à une classe sans modifier son code existant. Imaginez que vous disposez d'un système de traitement des paiements et que vous souhaitez ajouter un nouveau mode de paiement. Vous devriez pouvoir ajouter cette nouvelle méthode sans modifier le code existant.

Exemple :

from abc import ABC, abstractmethod

class PaymentProcessor(ABC):
    @abstractmethod
    def process_payment(self, amount):
        pass

class CreditCardPayment(PaymentProcessor):
    def process_payment(self, amount):
        print(f"Processing credit card payment of {amount}")

class PayPalPayment(PaymentProcessor):
    def process_payment(self, amount):
        print(f"Processing PayPal payment of {amount}")

# Usage
payments = [CreditCardPayment(), PayPalPayment()]
for payment in payments:
    payment.process_payment(100)

Dans cet exemple, PaymentProcessor est une classe abstraite qui définit un contrat pour le traitement des paiements. CreditCardPayment et PayPalPayment sont des implémentations qui étendent cette classe. Si vous souhaitez ajouter un nouveau mode de paiement, vous pouvez créer une nouvelle classe qui étend PaymentProcessor sans modifier les classes existantes.

3. L - Principe de substitution de Liskov (LSP)

Définition : Les sous-types doivent être substituables à leurs types de base sans altérer l'exactitude du programme.

Explication : Cela signifie que les objets d'une superclasse doivent être remplaçables par des objets d'une sous-classe sans affecter la fonctionnalité du programme. Par exemple, si vous avez une fonction qui fonctionne avec une classe Vehicle, elle devrait également fonctionner avec n'importe quelle sous-classe comme Car ou Bike.

Exemple :

class Vehicle:
    def start_engine(self):
        pass

class Car(Vehicle):
    def start_engine(self):
        print("Starting car engine...")

class Bike(Vehicle):
    def start_engine(self):
        print("Starting bike engine...")

# Usage
def start_vehicle_engine(vehicle: Vehicle):
    vehicle.start_engine()

car = Car()
bike = Bike()

start_vehicle_engine(car)  # Should work fine
start_vehicle_engine(bike) # Should work fine

Dans cet exemple, Car et Bike sont des sous-classes de Vehicle. La fonction start_vehicle_engine peut fonctionner avec n'importe quelle sous-classe de Vehicle sans avoir besoin de connaître les spécificités de la sous-classe, ce qui est conforme au principe de substitution de Liskov.

4. I - Principe de ségrégation d'interface (ISP)

Définition : Un client ne doit pas être obligé d'implémenter des interfaces qu'il n'utilise pas. Au lieu d'une seule grosse interface, de nombreuses petites interfaces sont préférées en fonction de groupes de méthodes, chacune servant un sous-module.

Explication : Ce principe suggère de créer des interfaces spécifiques pour chaque type de client plutôt qu'une seule interface à usage général. Imaginez que vous disposez d'un appareil capable d'imprimer, de numériser et de télécopier. Si vous disposez de machines distinctes qui peuvent uniquement imprimer ou numériser, elles ne devraient pas être obligées de mettre en œuvre des fonctionnalités qu'elles n'utilisent pas.

Exemple :

from abc import ABC, abstractmethod

class Printer(ABC):
    @abstractmethod
    def print(self, document):
        pass

class Scanner(ABC):
    @abstractmethod
    def scan(self, document):
        pass

class MultiFunctionDevice(Printer, Scanner):
    def print(self, document):
        print(f"Printing: {document}")

    def scan(self, document):
        print(f"Scanning: {document}")

# Usage
mfd = MultiFunctionDevice()
mfd.print("Document 1")
mfd.scan("Document 2")

Ici, l'imprimante et le scanner sont des interfaces distinctes. MultiFunctionDevice implémente les deux, mais s'il y avait des appareils qui imprimaient ou numérisaient uniquement, ils n'auraient pas besoin d'implémenter des méthodes qu'ils n'utilisent pas, en adhérant au principe de ségrégation des interfaces.

5. D - 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 (par exemple, des interfaces). Les abstractions ne devraient pas dépendre de détails. Les détails devraient dépendre des abstractions.

Explication : Au lieu d'une classe de haut niveau dépendant directement de classes de bas niveau, les deux devraient dépendre d'une interface ou d'une classe abstraite. Cela permet plus de flexibilité et un entretien plus facile.

Exemple :

from abc import ABC, abstractmethod

class NotificationService(ABC):
    @abstractmethod
    def send(self, message):
        pass

class EmailNotificationService(NotificationService):
    def send(self, message):
        print(f"Sending email: {message}")

class SMSNotificationService(NotificationService):
    def send(self, message):
        print(f"Sending SMS: {message}")

class NotificationSender:
    def __init__(self, service: NotificationService):
        self.service = service

    def notify(self, message):
        self.service.send(message)

# Usage
email_service = EmailNotificationService()
sms_service = SMSNotificationService()

notifier = NotificationSender(email_service)
notifier.notify("Hello via Email")

notifier = NotificationSender(sms_service)
notifier.notify("Hello via SMS")

Dans cet exemple, NotificationSender dépend de l'abstraction NotificationService plutôt que d'une classe concrète comme EmailNotificationService ou SMSNotificationService. De cette façon, vous pouvez changer de service de notification sans changer la classe NotificationSender.

Conclusion

  • Principe de responsabilité unique (SRP) : Une classe doit faire une chose et bien la faire.

  • Principe ouvert/fermé (OCP) : Une classe doit être ouverte pour extension mais fermée pour modification.

  • Principe de substitution de Liskov (LSP) : Les sous-classes doivent être substituables à leurs classes de base.

  • Principe de ségrégation des interfaces (ISP) : Aucun client ne devrait être obligé de dépendre de méthodes qu'il n'utilise pas.

  • Principe d'inversion de dépendance (DIP) : dépendez d'abstractions, pas d'implémentations concrètes.

En suivant ces principes SOLID, vous pouvez créer des logiciels plus faciles à comprendre, à maintenir et à étendre.

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