Patron Pont en Python
Le patron Bridge est un patron de conception structurel qui sépare l’abstraction de l’implémentation afin qu’elles puissent varier indépendamment. En termes simples, il consiste àséparer les multiples dimensions variables d’une chose, permettant à chaque dimension d’être étendue indépendamment.。
Métaphore du patron Bridge dans la vie quotidienne
Imaginez la relation entre une télécommande et un téléviseur :
- Télécommande(partie abstraction) : fournit une interface de contrôle (alimentation, volume, chaîne)
- Téléviseur(partie implémentation) : effectue réellement les opérations (Sony TV, Samsung TV, Xiaomi TV)
La télécommande n'a pas besoin de savoir de quel téléviseur il s'agit, et le téléviseur n'a pas besoin de savoir de quelle télécommande il s'agit. Ils sont reliés par un "pont" et peuvent être mis à niveau indépendamment.
Pourquoi avons-nous besoin du patron de conception Bridge ?
Limites de l'approche traditionnelle
Supposons que nous voulions concevoir un système de dessin graphique qui prend en charge différentes formes et couleurs :
Exemple
class RedCircle:
def draw(self):
print("Draw red circle")
class BlueCircle:
def draw(self):
print("Dessiner un cercle bleu")
class RedRectangle:
def draw(self):
print("Dessiner un rectangle rouge")
class BlueRectangle:
def draw(self):
print("Dessiner un rectangle bleu")
# Chaque fois qu'une couleur ou une forme est ajoutée, une nouvelle classe doit être créée
# 3 couleurs × 3 formes = 9 classes !
Avantages du patron Bridge
Exemple
class Color:
"""Interface Color - partie d'implémentation"""
def apply_color(self):
pass
class Shape:
"""Abstraction de Shape - partie abstraction"""
def __init__(self, color):
self.color = color # La clé du pontage !
def draw(self):
pass
# Il suffit de : 3 couleurs + 3 formes = 6 classes
# Au lieu de : 3 × 3 = 9 classes
Structure centrale du patron Pont
Comprenons les composants du patron Pont à travers un exemple complet :

Quatre rôles clés
- Abstraction: définit l'interface abstraite et maintient une référence à la partie implémentation
- Abstraction raffinée: étend l'interface abstraite
- Implémenteur: définit l'interface d'implémentation
- Implémenteur concret: implémente concrètement l'interface
Exemple de code complet : Système de dessin graphique
Étape 1 : Définir l'interface de la partie implémentation (Color)
Exemple
class Color(ABC):
"""Interface Color - partie d'implémentation"""
@abstractmethod
def apply_color(self):
pass
class Red(Color):
"""Implémentation concrète - Red"""
def apply_color(self):
return "Red"
class Blue(Color):
"""Implémentation concrète - Blue"""
def apply_color(self):
return "Blue"
class Green(Color):
"""Implémentation concrète - Vert"""
def apply_color(self):
return "Vert"
Étape 2 : Définir la partie abstraction (Shape)
Exemple
"""Abstraction de Shape - partie abstraction"""
def __init__(self, color: Color):
self.color = color # La clé de la liaison : garder une référence à la partie implémentation
@abstractmethod
def draw(self):
pass
class Circle(Shape):
"""Abstraction concrète - Cercle"""
def draw(self):
color_name = self.color.apply_color()
print(f"Dessiner un cercle en {color_name}")
class Rectangle(Shape):
"""Abstraction concrète - Rectangle"""
def draw(self):
color_name = self.color.apply_color()
print(f"Dessiner un rectangle en {color_name}")
class Triangle(Shape):
"""Abstraction concrète - Triangle"""
def draw(self):
color_name = self.color.apply_color()
print(f"Dessiner un triangle en {color_name}")
Étape 3 : Utiliser le patron Bridge
Exemple
def main():
# Créer l'implémentation de couleur
red = Rouge()
blue = Bleu()
green = Vert()
# Créer l'abstraction de forme et relier l'implémentation de couleur
red_circle = Circle(red)
blue_rectangle = Rectangle(blue)
green_triangle = Triangle(green)
# Dessiner la forme
print("=== Résultats du dessin ===")
red_circle.draw() # Sortie : Dessiner un cercle rouge
blue_rectangle.draw() # Sortie : Dessiner un rectangle bleu
green_triangle.draw() # Sortie : Dessiner un triangle vert
# Combinaison flexible : rectangle rouge
red_rectangle = Rectangle(red)
red_rectangle.draw() # Sortie : Dessiner un rectangle rouge
if __name__ == "__main__":
main()
Scénarios d'application pratiques
Scénario 1 : Système d'envoi de messages
Exemple
# Partie d'implémentation : méthode d'envoi de messages
class MessageSender(ABC):
@abstractmethod
def send(self, message):
pass
class EmailSender(MessageSender):
def send(self, message):
return f"Envoyer par e-mail : {message}"
class SMSSender(MessageSender):
def send(self, message):
return f"Envoyer par SMS : {message}"
class WeChatSender(MessageSender):
def send(self, message):
return f"Envoyer via WeChat : {message}"
# Partie abstraction : type de message
class Message(ABC):
def __init__(self, sender: MessageSender):
self.sender = sender
@abstractmethod
def send(self):
pass
class UrgentMessage(Message):
def send(self):
message = "[Urgent] " + self.get_content()
return self.sender.send(message)
def get_content(self):
return "Une exception système s'est produite, veuillez la traiter immédiatement !"
class NormalMessage(Message):
def __init__(self, sender: MessageSender, content):
super().__init__(sender)
self.content = content
def send(self):
return self.sender.send(self.content)
# Exemple d'utilisation
def message_example():
email_sender = EmailSender()
sms_sender = SMSSender()
# Message urgent envoyé par SMS
urgent_sms = UrgentMessage(sms_sender)
print(urgent_sms.send()) # Envoyer par SMS : [Urgent] Une exception système s'est produite, veuillez la traiter immédiatement !
# Message normal envoyé par e-mail
normal_email = NormalMessage(email_sender, "Le rapport mensuel a été généré")
print(normal_email.send()) # Envoyer par e-mail : Le rapport mensuel a été généré
message_example()
Scénario 2 : Système de contrôle à distance d'appareils
Exemple
class Device(ABC):
@abstractmethod
def turn_on(self):
pass
@abstractmethod
def turn_off(self):
pass
@abstractmethod
def set_volume(self, volume):
pass
class TV(Device):
def turn_on(self):
return "La TV est allumée"
def turn_off(self):
return "La TV est éteinte"
def set_volume(self, volume):
return f"Volume de la TV réglé à : {volume}"
class Radio(Device):
def turn_on(self):
return "La radio est allumée"
def turn_off(self):
return "La radio est éteinte"
def set_volume(self, volume):
return f"Volume de la radio réglé à : {volume}"
# Partie abstraction : télécommande
class RemoteControl:
def __init__(self, device: Device):
self.device = device
def toggle_power(self):
return "Basculer l'état d'alimentation"
def volume_up(self):
return "Augmenter le volume"
def volume_down(self):
return "Baisser le volume"
class AdvancedRemoteControl(RemoteControl):
def mute(self):
return "Mode silencieux"
def set_channel(self, channel):
return f"Passer à la chaîne : {channel}"
# Exemple d'utilisation
def remote_example():
tv = TV()
radio = Radio()
basic_remote = RemoteControl(tv)
advanced_remote = AdvancedRemoteControl(radio)
print(basic_remote.toggle_power()) # Basculer l'état d'alimentation
print(advanced_remote.mute()) # Mode silencieux
remote_example()
Comparaison des avantages du patron Bridge
| Fonctionnalité | Approche traditionnelle | Patron Pont |
|---|---|---|
| Évolutivité | Médiocre, nécessite de modifier le code existant | Bon, peut être étendu indépendamment |
| Nombre de classes | Nombreuses, propices à l'explosion des classes | Peu nombreux, croît linéairement |
| Kemudahan pemeliharaan | Difficile, un petit changement affecte tout | Facile, séparation des préoccupations |
| Flexibilité | Combinaisons fixes | Composition dynamique à l'exécution |
Bonnes pratiques et considérations
Quand l'utiliser
✅ Situations adaptées à l'utilisation du patron Bridge :
- Lorsque vous devez étendre indépendamment selon plusieurs dimensions
- Lorsque vous devez changer d'implémentation à l'exécution
- Lorsque vous devez éviter une liaison permanente entre l'abstraction et l'implémentation
- Lorsque l'héritage rendrait la hiérarchie de classes trop complexe
❌ Situations où il n'est pas adapté :
- Lorsqu'il y a peu de dimensions variables et qu'aucune conception complexe n'est nécessaire
- Lorsqu'il existe une relation de couplage forte entre l'abstraction et l'implémentation
Erreurs courantes et comment les éviter
Exemple
class BadShape:
def __init__(self, color_type):
if color_type == "red":
self.color = Red()
elif color_type == "blue":
self.color = Blue()
# Il faut encore modifier cette classe pour ajouter une nouvelle couleur
# Approche correcte : injection de dépendances
class GoodShape:
def __init__(self, color: Color): # Dépendre des interfaces, pas des implémentations concrètes
self.color = color
Exercices pratiques
Exercice 1 : Étendre le système graphique
Veuillez ajouter les fonctionnalités suivantes au système graphique :
- Ajouter une nouvelle couleur (jaune)
- Ajouter une nouvelle forme (ellipse)
- Créer une ellipse jaune et la dessiner
Exercice 2 : Concevoir un système de paiement
Concevoir un système de paiement avec les exigences suivantes :
- Moyens de paiement : Alipay, WeChat Pay, carte bancaire
- Types de paiement : paiement normal, paiement échelonné, paiement combiné
- Implémenté à l'aide du modèle Pont pour garantir que les méthodes de paiement et les types de paiement peuvent être étendus indépendamment
Résumé
Le modèle Pont, enséparant l'abstraction et l'implémentation,offre une grande flexibilité. Son idée centrale est :
- Identifier les dimensions de variation: Trouver plusieurs dimensions dans le système qui peuvent changer indépendamment
- Établir un pont: Connecter différentes dimensions par des relations de composition
- Expansion indépendante: Chaque dimension peut se développer et changer indépendamment