Patrons de conception Python

Les patrons de conception sont, pour des problèmes courants du développement logiciel,des solutions réutilisables. Ils ne sont pas des conceptions complètes qui peuvent être directement converties en code, mais plutôt, pour résoudre des problèmes spécifiques,des modèles ou des plans.。

Valeur essentielle des patrons de conception

Les patrons de conception sont comme des plans standard dans le domaine de la construction, offrant les avantages suivants pour le développement logiciel :

  • Améliorer la réutilisabilité du code: Éviter de réinventer la roue
  • Améliorer la maintenabilité du code: Rendre la structure du code plus claire
  • Favoriser la collaboration en équipe: Fournir un langage de programmation et une façon de penser unifiés
  • Améliorer la qualité du code: Les solutions testées en pratique sont plus fiables

Analogie des patrons de conception dans la vie quotidienne

Imaginez que vous allez construire une maison :

  • Manière primitive: Concevoir à partir de zéro à chaque fois est source d'erreurs et inefficace
  • Utilisation des patrons de conception: Utiliser des plans d'architecture standard, en connaissant les agencements standard pour le salon, la cuisine et la chambre

Les patrons de conception sont les « plans d'architecture standard » du développement logiciel.

Types de patrons de conception

Selon le livre classique« Design Patterns - Elements of Reusable Object-Oriented Software » (traduction chinoise : « Design Patterns: Reusable Object-Oriented Software Elements »)Selon sa définition, il existe 23 patrons de conception classiques. Ces patrons peuvent être divisés en trois grandes catégories selon leur objectif : les patrons de création, les patrons structurels et les patrons comportementaux. De plus, dans le développement d'entreprise, il existe également une classe couramment utilisée de patrons de conception architecturale.

NumberPatron et descriptionComprend
1 Patrons de création
Utilisés pour résoudre les problèmes de création d'objets, en encapsulant la logique d'instanciation, en masquant les détails d'implémentation concrets, ce qui rend le système plus flexible et extensible lors de la création d'objets.
  • Patron de conception Fabrique
  • Patron de conception Fabrique abstraite
  • Patron de conception Singleton
  • Patron Builder
  • Patron Prototype
2 Patrons structurels
Se concentrent sur la composition et la collaboration des classes et des objets, ce qui nous aide à construire des structures système flexibles, réutilisables et extensibles.
  • Patron Adaptateur
  • Patron Pont
  • Patron de filtre / critères
  • Patron Composite
  • Patron Décorateur
  • Patron Façade
  • Patron Poids-Mouche
  • Patron Proxy
3 Patrons comportementaux
Mettre l'accent sur la communication et l'attribution des responsabilités entre les objets, en améliorant la flexibilité et la maintenabilité du système grâce à l'encapsulation d'algorithmes, d'états ou de logique de requête.
  • Patron Chaîne de responsabilité
  • Patron Commande
  • Patron d'interpréteur
  • Patron Itérateur
  • Patron Médiateur
  • Patron Mémento
  • Patron Observateur
  • Patron État
  • Patron Objet Nul
  • Patron Stratégie
  • Patron de méthode de gabarit
  • Patron Visiteur
4 Patrons d'architecture de niveau entreprise
Ces patrons sont couramment utilisés dans les architectures en couches des grands systèmes, en mettant l'accent sur la collaboration entre la couche de présentation et la couche de logique métier.
  • Patron MVC (Modèle-Vue-Contrôleur)
  • Patron Business Delegate
  • Patron Entité Composite
  • Patron d'objet d'accès aux données
  • Modèle de contrôleur frontal
  • Patron Filtre Intercepteur
  • Patron Localisateur de services
  • Patron d'objet de transfert

L'image ci-dessous montre les relations et la structure hiérarchique entre les différents patrons de conception :

设计模式之间的关系

Avantages des patrons de conception

  • Fournir un langage de conception unifié, permettant aux développeurs de communiquer rapidement l'intention de conception.
  • Fournir des solutions matures et réutilisables aux problèmes courants.
  • Réduire le couplage du système et améliorer la maintenabilité et l'extensibilité du code.
  • Réduire la conception redondante, en améliorant l'efficacité de développement et la qualité du code.
  • Aider les nouveaux membres à comprendre rapidement l'architecture du système et la philosophie de conception.

Six principes des patrons de conception

1. Principe ouvert/fermé

Ouvert à l'extension, fermé à la modification. Un logiciel doit s'adapter aux changements en étendant de nouvelles fonctionnalités plutôt qu'en modifiant le code existant. Le cœur de la mise en œuvre de ce principe réside dans l'utilisation d'abstractions (interfaces ou classes de base) pour définir un comportement stable.

2. Principe de substitution de Liskov

Les sous-classes doivent pouvoir remplacer tout endroit où une classe de base apparaît. L'héritage n'a de sens que lorsque les classes dérivées peuvent complètement remplacer la classe de base. Ce principe garantit la correction du polymorphisme et constitue un complément important au principe ouvert/fermé.

3. Principe d'inversion des dépendances

Les modules de haut niveau ne doivent pas dépendre des modules de bas niveau ; les deux doivent dépendre d'abstractions. Les implémentations concrètes doivent dépendre d'interfaces ou de classes abstraites plutôt que directement de classes concrètes. Ce principe rend le système plus facile à étendre et à tester.

4. Principe de ségrégation des interfaces

Une classe ne doit pas dépendre d'interfaces dont elle n'a pas besoin. Les interfaces volumineuses doivent être divisées en interfaces plus petites et plus spécifiques afin de réduire le couplage et d'améliorer la flexibilité.

5. Loi de Déméter

Aussi appelé « principe du moindre savoir » : un objet doit minimiser ses interactions avec d'autres objets. En d'autres termes, une classe ne doit connaître que les objets directement liés à elle, ce qui réduit la complexité du système.

6. Principe de réutilisation par composition

Privilégiez l'utilisation de relations de composition ou d'agrégation pour la réutilisation plutôt que l'héritage. L'héritage crée un fort couplage, tandis que la composition permet un remplacement flexible des dépendances à l'exécution, ce qui aboutit à une conception structurelle plus élégante.


Exemples courants de patrons de conception en Python

Comprenons l'application des patrons de conception en Python à travers quelques exemples concrets.

Patron de conception Singleton

Le patron Singleton garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès global.

Exemple

class DatabaseConnection:
    _instance = None
   
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            print("Création d'une nouvelle connexion à la base de données")
        return cls._instance
   
    def connect(self):
        print("Connexion à la base de données")

# Exemple d'utilisation
db1 = DatabaseConnection()
db2 = DatabaseConnection()

print(f"db1 et db2 sont-ils la même instance ? {db1 is db2}")  # Sortie : True

Scénarios d'application:

  • Pool de connexions à la base de données
  • Gestionnaire de configuration
  • Logger

Patron de conception Fabrique

Le patron de conception Fabrique fournit une interface pour créer des objets, permettant aux sous-classes de décider quelle classe instancier.

Exemple

from abc import ABC, abstractmethod

# Produit abstrait
class Notification(ABC):
    @abstractmethod
    def send(self, message: str):
        pass

# Produit concret
class EmailNotification(Notification):
    def send(self, message: str):
        print(f"Sending email: {message}")

class SMSNotification(Notification):
    def send(self, message: str):
        print(f"Sending SMS: {message}")

# Classe Fabrique
class NotificationFactory:
    @staticmethod
    def create_notification(notification_type: str) -> Notification:
        if notification_type == "email":
            return EmailNotification()
        elif notification_type == "sms":
            return SMSNotification()
        else:
            raise ValueError("Type de notification non pris en charge")

# Exemple d'utilisation
email = NotificationFactory.create_notification("email")
sms = NotificationFactory.create_notification("sms")

email.send("Votre commande a été expédiée")
sms.send("Code de vérification : 123456")

Explication de code:

  • Notificationest une classe de base abstraite qui définit l'interface
  • EmailNotificationetSMSNotificationest l'implémentation concrète
  • NotificationFactoryest responsable de la création d'objets de notification concrets

Patron Observateur

Le patron Observateur définit une dépendance de type un-à-plusieurs entre des objets. Lorsque l'état d'un objet change, tous les objets qui en dépendent sont notifiés.

Exemple

from abc import ABC, abstractmethod

# Interface Observateur
class Observer(ABC):
    @abstractmethod
    def update(self, message: str):
        pass

# Observateur concret
class EmailSubscriber(Observer):
    def __init__(self, name: str):
        self.name = name
   
    def update(self, message: str):
        print(f"{self.name} a reçu une notification par e-mail : {message}")

class SMSSubscriber(Observer):
    def __init__(self, name: str):
        self.name = name
   
    def update(self, message: str):
        print(f"{self.name} a reçu une notification par SMS : {message}")

# Sujet (Observable)
class NewsPublisher:
    def __init__(self):
        self._subscribers = []
   
    def subscribe(self, subscriber: Observer):
        self._subscribers.append(subscriber)
   
    def unsubscribe(self, subscriber: Observer):
        self._subscribers.remove(subscriber)
   
    def notify_subscribers(self, message: str):
        for subscriber in self._subscribers:
            subscriber.update(message)

# Exemple d'utilisation
publisher = NewsPublisher()

# Créer des abonnés
alice = EmailSubscriber("Alice")
bob = SMSSubscriber("Bob")

# S'abonner aux actualités
publisher.subscribe(alice)
publisher.subscribe(bob)

# Publier les actualités
publisher.notify_subscribers("Python 3.12 est sorti !")

# Se désabonner
publisher.unsubscribe(alice)
publisher.notify_subscribers("Ceci est une notification que seul Bob peut voir")

Considérations particulières pour les patrons de conception en Python

La nature dynamique de Python

En tant que langage dynamique, Python rend certains patrons de conception plus simples à implémenter :

Exemple

# Fabrique simple de style Python
def create_payment(method):
    payment_methods = {
        'credit_card': CreditCardPayment,
        'paypal': PayPalPayment,
        'alipay': AlipayPayment
    }
    return payment_methods[method]()

# À utiliser directement, aucune hiérarchie de classes complexe nécessaire
payment = create_payment('alipay')

Prise en charge intégrée du patron Decorator

Python dispose d'une syntaxe de décorateur intégrée, ce qui rend le patron Decorator plus élégant à implémenter :

Exemple

def log_execution_time(func):
    def wrapper(*args, **kwargs):
        import time
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} temps d'exécution: {end - start:.2f} secondes")
        return result
    return wrapper

@log_execution_time
def process_data(data):
    # Simuler le traitement des données
    import time
    time.sleep(1)
    return f"Données traitées: {data}"

# Utilisation
result = process_data("Données d'exemple")
Autres extensions