Patron Monteur en Python
Le modèle Builder nous permet de créer différentes représentations d'objets en utilisant le même processus de construction. Imaginez la construction d'une maison : que vous construisiez une villa ou un appartement, vous devez passer par des étapes telles que la pose des fondations, la construction des murs et l'installation de la plomberie et des systèmes électriques, mais les résultats finaux sont complètement différents.
Idée centrale
Le modèle Builder sépare le processus de construction d'un objet complexe de sa représentation, permettant au même processus de construction de créer différentes représentations. Ce modèle est particulièrement adapté à la création d'objets complexes composés de plusieurs composants.
Pourquoi avons-nous besoin du modèle Builder ?
Problèmes avec les méthodes de construction traditionnelles
Regardons d'abord un exemple qui n'utilise pas le modèle Builder :
Exemple
def __init__(self, cpu, memory, storage, graphics_card, monitor):
self.cpu = cpu
self.memory = memory
self.storage = storage
self.graphics_card = graphics_card
self.monitor = monitor
def __str__(self):
return f"Ordinateur : CPU={self.cpu}, Mémoire={self.memory}GB, Stockage={self.storage}GB, Graphiques={self.graphics_card}, Moniteur={self.monitor}"
# Créer un objet ordinateur
computer = Computer("Intel i7", 16, 512, "NVIDIA RTX 3060", "27 pouces 4K")
print(computer)
Inconvénients de cette approche :
- Trop de paramètres de constructeur, ce qui rend la maintenance difficile
- Il faut se souvenir de l'ordre de tous les paramètres
- Le processus de création n'est pas assez flexible
- Mauvaise lisibilité du code
Composants du patron Builder
Le patron Builder implique quatre rôles principaux :

1. Product
L'objet complexe à créer
2. Builder (Builder abstrait)
Définit l'interface abstraite pour créer les différentes parties d'un produit
3. ConcreteBuilder (Builder concret)
Implémente l'interface Builder, construisant et assemblant les différentes parties
4. Directeur
Construit un objet qui utilise l'interface Builder
Implémentation complète du code
Implémentons entièrement le patron Builder avec un exemple de configuration d'ordinateur:
1. Classe Produit
Exemple
"""Classe Produit Computer"""
def __init__(self):
self.cpu = None
self.memory = None
self.storage = None
self.graphics_card = None
self.monitor = None
def __str__(self):
specs = []
if self.cpu:
specs.append(f"Processeur : {self.cpu}")
if self.memory:
specs.append(f"Mémoire : {self.memory}GB")
if self.storage:
specs.append(f"Stockage : {self.storage}GB")
if self.graphics_card:
specs.append(f"Carte graphique : {self.graphics_card}")
if self.monitor:
specs.append(f"Écran : {self.monitor}")
return "Configuration de l'ordinateur :\n" + "\n".join(f" - {spec}" for spec in specs)
2. Monteur abstrait (Builder)
Exemple
class ComputerBuilder(ABC):
"""Classe abstraite de constructeur d'ordinateur"""
def __init__(self):
self.computer = Computer()
@abstractmethod
def build_cpu(self):
pass
@abstractmethod
def build_memory(self):
pass
@abstractmethod
def build_storage(self):
pass
@abstractmethod
def build_graphics_card(self):
pass
@abstractmethod
def build_monitor(self):
pass
def get_computer(self):
return self.computer
3. ConcreteBuilder (Builder concret)
Exemple
"""Constructeur d'ordinateur de jeu"""
def build_cpu(self):
self.computer.cpu = "Intel i9-13900K"
def build_memory(self):
self.computer.memory = 32
def build_storage(self):
self.computer.storage = 2000 # 2TB
def build_graphics_card(self):
self.computer.graphics_card = "NVIDIA RTX 4090"
def build_monitor(self):
self.computer.monitor = "32-inch 4K 144Hz"
class OfficeComputerBuilder(ComputerBuilder):
"""Constructeur d'ordinateur de bureau"""
def build_cpu(self):
self.computer.cpu = "Intel i5-13400"
def build_memory(self):
self.computer.memory = 16
def build_storage(self):
self.computer.storage = 512
def build_graphics_card(self):
self.computer.graphics_card = "Graphiques intégrés"
def build_monitor(self):
self.computer.monitor = "24-inch 1080P"
4. Directeur
Exemple
"""Directeur de construction d'ordinateur"""
def __init__(self, builder):
self.builder = builder
def construct_computer(self):
"""Le processus complet de construction d'un ordinateur"""
self.builder.build_cpu()
self.builder.build_memory()
self.builder.build_storage()
self.builder.build_graphics_card()
self.builder.build_monitor()
def get_computer(self):
return self.builder.get_computer()
Exemple d'utilisation
Voyons maintenant comment utiliser le patron Builder pour créer différents types d'ordinateurs :
Exemple
print("=== Démo du patron Builder ===\n"\n")
# Créer un ordinateur de jeu
print("1. Construction de l'ordinateur de jeu :")
gaming_builder = GamingComputerBuilder()
director = ComputerDirector(gaming_builder)
director.construct_computer()
gaming_computer = director.get_computer()
print(gaming_computer)
print("\n" + "="*50 + "\n")
# Créer un ordinateur de bureau
print("2. Construction de l'ordinateur de bureau :")
office_builder = OfficeComputerBuilder()
director = ComputerDirector(office_builder)
director.construct_computer()
office_computer = director.get_computer()
print(office_computer)
print("\n" + "="*50 + "\n")
# Créer un ordinateur personnalisé
print("3. Assemblage d'un ordinateur personnalisé :")
custom_builder = CustomComputerBuilder()
custom_builder.build_cpu("AMD Ryzen 7 7800X3D")
custom_builder.build_memory(64)
custom_builder.build_storage(4000)
custom_builder.build_graphics_card("AMD RX 7900 XTX")
custom_builder.build_monitor("moniteur incurvé ultra-large de 34 pouces")
custom_computer = custom_builder.get_computer()
print(custom_computer)
# Constructeur personnalisé (construction avec des étapes optionnelles)
class CustomComputerBuilder:
"""Constructeur d'ordinateur personnalisé"""
def __init__(self):
self.computer = Computer()
def build_cpu(self, cpu):
self.computer.cpu = cpu
return self # Renvoyer self pour prendre en charge les appels chaînés
def build_memory(self, memory):
self.computer.memory = memory
return self
def build_storage(self, storage):
self.computer.storage = storage
return self
def build_graphics_card(self, graphics_card):
self.computer.graphics_card = graphics_card
return self
def build_monitor(self, monitor):
self.computer.monitor = monitor
return self
def get_computer(self):
return self.computer
if __name__ == "__main__":
main()
Variantes du patron Builder
1. Builder à interface fluide
Exemple
def __init__(self):
self.size = None
self.cheese = False
self.pepperoni = False
self.mushrooms = False
def __str__(self):
ingredients = []
if self.cheese: ingredients.append("Cheese")
if self.pepperoni: ingredients.append("Italian sausage")
if self.mushrooms: ingredients.append("Champignons")
return f"{self.size}-inch pizza, garnitures : {', '.join(ingredients)}"
class PizzaBuilder:
def __init__(self, size):
self.pizza = Pizza()
self.pizza.size = size
def add_cheese(self):
self.pizza.cheese = True
return self
def add_pepperoni(self):
self.pizza.pepperoni = True
return self
def add_mushrooms(self):
self.pizza.mushrooms = True
return self
def build(self):
return self.pizza
# Utiliser l'interface fluide
pizza = (PizzaBuilder(12)
.add_cheese()
.add_pepperoni()
.add_mushrooms()
.build())
print(pizza)
Avantages et inconvénients du patron Builder
Avantages
| Avantages | Description |
|---|---|
| Bonne encapsulation | Le processus de construction est séparé de la représentation ; les clients n'ont pas besoin de connaître les détails internes. |
| Forte extensibilité | De nouveaux constructeurs concrets peuvent être ajoutés facilement. |
| Un meilleur contrôle | Permet un contrôle plus fin du processus de construction. |
| Lisibilité du code | Le code est plus clair lorsqu'on utilise des appels chaînés. |
Inconvénients
| Inconvénients | Description |
|---|---|
| Ajoute de la complexité | Nécessite de définir plusieurs classes, ce qui augmente la complexité du système. |
| Ne convient pas lorsque les produits diffèrent fortement | Si les produits diffèrent fortement les uns des autres, le patron Builder n'est pas très adapté. |
| Les Builders dépendent du produit | Le Builder doit connaître les détails spécifiques du produit. |
Scénarios applicables
Cas adaptés au patron Builder :
- Création d'objets complexes: besoin de créer des objets complexes composés de plusieurs parties.
- Processus de construction stable: le processus de construction est relativement stable, mais les composants de l'objet changent fréquemment.
- Représentations multiples: l'objet à créer possède plusieurs représentations différentes.
- Éviter les constructeurs qui se chevauchent: éviter d'utiliser des constructeurs avec de nombreux paramètres.
Exemples d'applications pratiques :
- Convertisseur de documents: convertir des documents vers différents formats tels que PDF, Word, HTML.
- Préparation de repas: préparer des repas pour différents ensembles (combinaisons de burgers, frites, boissons).
- Construction de composants d'interface utilisateur: construire des composants complexes d'interface utilisateur.
- Génération de rapports: générer des rapports contenant différentes parties (en-tête, données, graphiques).
Exercices pratiques
Exercice 1 : implémenter un constructeur de voiture
Essayez d'implémenter un patron de construction de voiture avec les composants suivants :
- Moteur (essence, électrique, hybride)
- Couleur de carrosserie (rouge, bleu, noir, blanc)
- Taille des roues (16 pouces, 18 pouces, 20 pouces)
- Intérieur (cuir, tissu)
Exercice 2 : Améliorer le Computer Builder
Ajoutez les fonctionnalités suivantes au Computer Builder :
- Composants optionnels (par exemple, s'il faut installer un lecteur optique)
- Logique de validation (par exemple, la mémoire ne peut pas être inférieure à 4GB)
- Fonction de calcul du prix
Résumé
Le patron Builder est un puissant patron de conception de création. En décomposant le processus de construction d'un objet complexe en plusieurs étapes simples, il rend le processus de création plus flexible et plus contrôlable. Bien qu'il augmente la complexité du système, dans les scénarios où des objets complexes doivent être créés et où le processus de construction est relativement fixe, le patron Builder peut considérablement améliorer la maintenabilité et l'évolutivité du code.
Rappelez-vous l'idée centrale des patrons de conception :N'utilisez pas les patrons pour le simple plaisir de les utiliser, mais pour résoudre des problèmes de conception spécifiques.En développement réel, décidez d'utiliser ou non le patron Builder en fonction des exigences spécifiques.
Autres extensions