Maison > Article > développement back-end > Principes SOLID - Expliqués à l'aide d'exemples réels en Python
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.
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.
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.
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.
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.
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.
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!