Quels sont quelques exemples de modèles de conception simples qui peuvent être utilisés dans divers projets ?
Bon, explorons quelques modèles de conception simples qui peuvent être utiles dans divers projets. Ces modèles sont relativement faciles à comprendre et à mettre en œuvre, et ils peuvent améliorer considérablement la qualité, la maintenabilité et la réutilisation du code.
1. Singleton
* Objectif : Garantit qu’une seule instance d’une classe est créée et fournit un point d’accès global à cette instance.
* Quand l'utiliser : Lorsque vous avez besoin d'une ressource unique et partagée (par exemple, gestionnaire de configuration, pool de connexions à la base de données, enregistreur).
print(instance1 est instance2) # Sortie :True (il s'agit du même objet)
```
* Remarques : Soyez prudent avec les Singletons dans les environnements fortement multithread. Vous devrez peut-être ajouter des mécanismes de verrouillage pour garantir la sécurité des threads lors de la création de l'instance. La surutilisation des Singletons peut conduire à un couplage étroit et rendre les tests difficiles.
2. Méthode d'usine
* Objectif : Définit une interface pour créer un objet, mais laisse les sous-classes décider quelle classe instancier. Il dissocie le code client de la classe spécifique en cours de création.
* Quand l'utiliser : Lorsque vous devez créer des objets de différents types en fonction d'une condition ou d'une configuration et que vous souhaitez éviter de coder en dur la logique de création d'objet directement dans le code client.
* Exemple simple (Python) :
```python
Bouton de classe :
def rendu (auto):
lever NotImplementedError()
classe HTMLButton(Bouton):
def rendu (auto):
return "Bouton HTML "
classe WindowsButton(Bouton):
def rendu (auto):
return "Bouton Windows (spécifique à l'interface utilisateur)"
classe ButtonFactory :
def create_button(self, bouton_type) :
si bouton_type =="html":
retourner le bouton HTML()
elif bouton_type =="windows":
retourner le bouton Windows()
autre:
raise ValueError("Type de bouton invalide")
# Utilisation
usine =ButtonFactory()
html_button =usine.create_button("html")
windows_button =usine.create_button("windows")
print(html_button.render()) # Sortie :
print(windows_button.render()) # Sortie :bouton Windows (spécifique à l'interface utilisateur)
```
* Remarques : La Méthode Factory permet d'ajouter de nouveaux types de boutons sans modifier directement la classe `ButtonFactory` (principe ouvert/fermé).
3. Stratégie
* Objectif : Définit une famille d’algorithmes, encapsule chacun d’eux et les rend interchangeables. La stratégie permet à l'algorithme de varier indépendamment des clients qui l'utilisent.
* Quand l'utiliser : Lorsque vous disposez de plusieurs façons d’effectuer une tâche spécifique et que vous souhaitez pouvoir basculer facilement entre elles au moment de l’exécution.
* Remarques : Le « ShoppingCart » n'a pas besoin de connaître le mode de paiement spécifique. Il utilise simplement le « PaymentStrategy » injecté pour effectuer le paiement. Cela facilite l'ajout de nouveaux modes de paiement sans modifier la classe `ShoppingCart`.
4. Observateur
* Objectif : Définit une dépendance un-à-plusieurs entre les objets afin que lorsqu'un objet change d'état, toutes ses dépendances soient notifiées et mises à jour automatiquement.
* Quand l'utiliser : Lorsqu'une modification d'un objet nécessite la modification d'autres objets et que vous ne souhaitez pas que les objets soient étroitement couplés. Exemples :gestion des événements, mises à jour de l'interface utilisateur, architecture modèle-vue-contrôleur (MVC).
* Exemple simple (Python) :
```python
Sujet de classe :
def __init__(soi) :
self._observateurs =[]
def attach(soi, observateur) :
self._observers.append(observateur)
def détacher (soi, observateur):
self._observers.remove(observateur)
def notify (soi, message):
pour l'observateur dans self._observers :
observateur.update(message)
Observateur de classe :
def update (soi, message) :
lever NotImplementedError()
classe ConcreteObserverA (Observateur):
def update (soi, message) :
print(f"Observateur A reçu :{message}")
classe ConcreteObserverB (Observateur):
def update (soi, message) :
print(f"Observateur B reçu :{message.upper()}")
# Utilisation
sujet =Sujet()
observer_a =ConcreteObserverA()
observer_b =ConcreteObserverB()
sujet.attacher(observer_a)
sujet.attacher(observer_b)
subject.notify("Bonjour tout le monde !") # Sortie :Observateur A reçu :Bonjour tout le monde !
# Observateur B reçu :BONJOUR LE MONDE !
sujet.detach(observer_a)
subject.notify("Au revoir!") # Sortie :L'observateur B a reçu :ADIEU !
```
* Remarques : Le « Sujet » maintient une liste d'« Observateurs ». Lorsque l'état du « Sujet » change (dans ce cas, lorsque « notify » est appelé), il parcourt la liste et appelle la méthode « update » sur chaque « Observateur ».
5. Méthode de modèle
* Objectif : Définit le squelette d'un algorithme dans une classe de base mais permet aux sous-classes de remplacer des étapes spécifiques de l'algorithme sans modifier sa structure.
* Quand l'utiliser : Lorsque vous disposez d’un ensemble d’étapes qui doivent être effectuées dans un ordre spécifique, certaines de ces étapes peuvent varier en fonction de l’implémentation spécifique.
* Exemple simple (Python) :
```python
classe DataProcessor :
def process_data(soi) :
self.read_data()
self.validate_data()
self.transform_data()
self.save_data()
print("Traitement des données terminé.")
def read_data(soi) :
lever NotImplementedError()
def validate_data(soi) :
print("Validation par défaut :vérification des valeurs nulles.")
def transform_data(soi) :
lever NotImplementedError()
def save_data(soi) :
lever NotImplementedError()
classe CSVDataProcessor(DataProcessor) :
def read_data(soi) :
print("Lecture des données du fichier CSV.")
def transform_data(soi) :
print("Transformation des données CSV.")
def save_data(soi) :
print("Enregistrement des données dans la base de données.")
classe JSONDataProcessor(DataProcessor) :
def read_data(soi) :
print("Lecture des données du fichier JSON.")
def validate_data(soi) :
print("Validation personnalisée pour les données JSON :vérification du schéma.")
def transform_data(soi) :
print("Transformation des données JSON.")
def save_data(soi) :
print("Enregistrement des données dans un fichier JSON.")
# Utilisation
csv_processor =CSVDataProcessor()
csv_processor.process_data()
# Sortir:
# Lecture des données du fichier CSV.
# Validation par défaut :vérification des valeurs nulles.
# Transformation des données CSV.
# Sauvegarde des données dans la base de données.
# Traitement des données terminé.
json_processor =JSONDataProcessor()
json_processor.process_data()
# Sortir:
# Lecture des données du fichier JSON.
# Validation personnalisée pour les données JSON :vérification du schéma.
# Transformation des données JSON.
# Sauvegarde des données dans un fichier JSON.
# Traitement des données terminé.
```
* Remarques : Le `DataProcessor` définit la structure globale de l'algorithme de traitement des données. Les sous-classes telles que « CSVDataProcessor » et « JSONDataProcessor » fournissent des implémentations spécifiques pour les étapes « read_data », « transform_data » et « save_data ». L'étape `validate_data` peut être remplacée ou utiliser l'implémentation par défaut.
6. Décorateur
* Objectif : Ajoute dynamiquement des responsabilités à un objet sans modifier sa classe. Les décorateurs offrent une alternative flexible au sous-classement pour étendre les fonctionnalités.
* Quand l'utiliser : Lorsque vous souhaitez ajouter des fonctionnalités à un objet au moment de l'exécution, sans affecter les autres objets de la même classe. Utile pour ajouter une journalisation, une mise en cache ou une autorisation.
* Remarques : Le `CoffeeDecorator` fournit une classe de base pour les décorateurs. Chaque décorateur (par exemple, `MilkDecorator`, `SugarDecorator`) encapsule l'objet `Coffee` d'origine et ajoute sa propre fonctionnalité (dans ce cas, en ajoutant le coût et la description).
Considérations clés pour choisir un modèle :
* Comprendre le problème : Définissez clairement le problème que vous essayez de résoudre avant de rechercher un modèle. N'appliquez pas aveuglément des modèles ; utilisez-les comme outils pour améliorer votre code.
* Simplicité : Commencez par la solution la plus simple qui répond à vos besoins. Ne sur-concevez pas les choses prématurément.
* Contexte : Le meilleur modèle dépend du contexte spécifique de votre projet, du langage que vous utilisez et de la base de code existante.
* Test : Les modèles de conception doivent rendre votre code plus testable, pas moins. Assurez-vous que vous pouvez facilement écrire des tests unitaires pour les composants que vous utilisez.
Ce ne sont là que quelques exemples de modèles de conception simples. Au fur et à mesure que vous gagnerez en expérience, vous apprendrez à reconnaître les situations dans lesquelles ces modèles, ainsi que d'autres, peuvent être appliqués pour créer un code plus robuste, plus maintenable et réutilisable. Bonne chance!