Design Patterns (Patrons de Conception)
Introduction au Module
Ce module couvre les patrons de conception (design patterns), des solutions abstraites et réutilisables pour résoudre des problèmes courants en architecture logicielle.
Contenu du Module
- Introduction au concept de design patterns
- Patrons de base
- Classification par familles :
- Patrons de génération (Creational)
- Patrons de structuration (Structural)
- Patrons de comportement (Behavioral)
- Patrons concurrents
- Autres patrons
Origine des Design Patterns
Les design patterns trouvent leurs racines dans les travaux de Christopher Alexander sur l'architecture et l'urbanisme. Le livre emblématique "Design Patterns: Elements of Reusable Object-Oriented Software" (Gang of Four) propose des solutions abstraites adaptables à divers contextes.
Chaque patron décrit un problème récurrent et sa solution, de manière à pouvoir l'appliquer un million de fois sans jamais la reproduire exactement de la même façon (GoF).
Objectifs du Module
Les objectifs principaux sont :
- Maîtriser des patrons de conception réutilisables dans vos projets
- Faciliter la mémorisation et la compréhension des patrons
- Permettre leur application concrète
Ce module se concentre sur les patrons de conception (micro patrons) en Java, car plusieurs sont déjà intégrés dans le langage. D'autres langages et ressources peuvent aussi offrir des informations complémentaires.
Styles, Patrons et Idiomes
En architecture logicielle, il existe trois concepts distincts :
- Styles (macro patrons) : Structures de haut niveau pour décrire un logiciel dans son ensemble. Exemples : modèle par couches (layered), Model-View-Controller (MVC).
- Patrons (micro patrons) : Solutions abstraites réutilisables dans différents contextes, offrant les mêmes avantages à chaque application.
- Idiomes : Bonnes pratiques spécifiques à un formalisme de programmation (ex : construction d'une bonne classe en POO) ou à un langage (ex : destruction d'objets en C++).
Description d'un Patron de Conception
Un patron de conception est défini par :
- Nom : Identifie le patron.
- Problème : Décrit le problème et son contexte, parfois avec une liste des anti-patterns (mauvaises pratiques) à éviter.
- Solution : Présente les éléments de conception et leurs relations, ainsi que les responsabilités et collaborations.
- Conséquences : Analyse des compromis entre temps et espace mémoire, ainsi que des problèmes liés au langage ou à l'implémentation.
Catégories de Design Patterns
Les patrons de conception sont classés en trois familles principales :
| Famille | Patrons |
|---|---|
| Génération |
|
| Structuration |
|
| Comportement |
|
Patrons de Base en POO
| Patron | Description |
|---|---|
| Interface | Encapsule un service offert par plusieurs classes. Permet d'éviter la redondance en définissant une abstraction commune. |
| Classe abstraite parent | Permet différentes définitions de services pour des classes similaires. Les sous-classes implémentent les méthodes abstraites et héritent des méthodes concrètes. |
| Méthodes privées | Limitent l'accès au comportement interne d'une instance, renforçant l'encapsulation. |
| Méthodes accesseurs | Contrôlent l'accès aux propriétés des instances d'une classe. Permettent de gérer les changements d'état et d'assurer la cohérence. |
| Gestionnaire de constantes | Centralise et facilite la gestion des constantes d'une application (ex : noms de fichiers, messages d'erreur). Évite la dispersion des données. |
| Objet immuable | Garantit qu'une instance ne peut pas être modifiée après sa création. Utilise des attributs final et interdit les méthodes setXXX. |
| Moniteur | Contrôle l'accès concurrent à une ressource partagée pour éviter des comportements imprévisibles (ex : synchronisation en Java). |
Exemple : Patron Interface
En Java, le patron Interface permet de définir un contrat commun pour plusieurs classes. Par exemple, pour calculer le salaire de différents types d'employés, on peut créer une interface SalaryCalculator implémentée par chaque catégorie d'employé.
Avantage : Le code client n'a pas besoin d'être modifié si une nouvelle classe de calcul de salaire est ajoutée.
Exemple : Patron Classe Abstraite Parent
Une classe abstraite parent définit une structure commune avec des méthodes abstraites (à implémenter par les sous-classes) et des méthodes concrètes (déjà implémentées). Par exemple, une classe Employee abstraite peut avoir une méthode abstraite computeCompensation() pour calculer la rémunération, tandis que les sous-classes comme CategoryA ou CategoryB fournissent leur propre implémentation.
Différence avec une interface : Une classe abstraite peut contenir des attributs et des méthodes concrètes, contrairement à une interface.
Exemple : Patron Méthodes Accesseurs
Les méthodes accesseurs (getXXX, setXXX) permettent de contrôler l'accès aux propriétés d'une instance. Par exemple, pour un étudiant, on peut utiliser getStudentNumber() pour accéder au numéro étudiant sans exposer directement l'attribut.
Avantages :
- Limite les modifications et favorise la maintenance
- Permet de changer la représentation interne sans impacter le client
- Cache si l'attribut est direct ou dérivé (ex : moyenne cumulative)
Exemple : Patron Objet Immuable
Un objet immuable est final et ses attributs sont déclarés final. Aucune méthode setXXX n'est disponible. Les données sont passées au constructeur.
Avantages :
- Évite les problèmes de synchronisation dans un environnement multithread
- Empêche la dérivation d'une sous-classe avec un comportement différent
- Permet de faire des copies des références pour éviter les effets de bord
Exemple : Patron Moniteur
Le patron Moniteur assure la consistance d'une ressource partagée en multithreading. En Java, cela se fait via la synchronisation des méthodes. Par exemple, pour écrire dans un fichier de journalisation (log.txt), on utilise synchronized pour éviter les écrasements.
Patrons de Génération
Ces patrons visent à abstraire le processus de création d'objets et à rendre le système indépendant de cette création.
| Patron | Description |
|---|---|
| Factory Method | Création d'une instance de plusieurs classes dérivées via une méthode. Permet de déléguer la création à des sous-classes. |
| Singleton | Création d'une seule instance d'une classe. Assure un accès global et consistant. |
| Factory abstrait | Création d'instances de plusieurs familles de classes. Encapsule la logique de sélection des familles. |
| Prototype | Création d'une instance par copie ou clonage d'un prototype existant. |
| Builder | Création incrémentale d'une instance complexe. |
Exemple : Patron Singleton
Le patron Singleton garantit qu'une classe ne peut avoir qu'une seule instance. Par exemple, pour un fichier de journalisation (FileLogger), on utilise un constructeur privé et une méthode statique getFileLogger() pour obtenir l'instance unique.
Avantages :
- Évite la création de plusieurs instances coûteuses
- Assure un accès global et consistant
- Centralise la gestion de l'instance
Exemple : Patron Factory Method
La Factory Method encapsule la création d'une classe dans une méthode, permettant au client de travailler avec une abstraction. Par exemple, pour la journalisation d'une application, on peut utiliser un fichier de propriétés (Logger.properties) pour déterminer le média (fichier ou console) et instancier la classe appropriée (FileLogger ou ConsoleLogger).
Exemple : Patron Abstract Factory
L'Abstract Factory permet de créer des instances de plusieurs familles de classes. Par exemple, si un service nécessite des classes FileLogger et ConsoleLogger pour fonctionner, l'Abstract Factory encapsule la logique de sélection et d'instanciation de ces familles.
Avantages :
- Cache la logique de sélection des produits
- Uniformise l'instanciation des produits
- Permet l'échange de familles de classes
- Favorise la consistance des produits dans une famille
FAQ
Pourquoi utiliser des patrons de conception plutôt que de l'héritage ?
Les interfaces et classes abstraites sont préférables à l'héritage multiple car elles évitent les conflits de méthodes et permettent une meilleure modularité et réutilisabilité du code.
Comment gérer les constantes dans une application ?
Centralisez-les dans une classe dédiée (ConstantDataManager) pour faciliter leur maintenance et leur accès par différentes parties du code.
Quels sont les avantages d'un objet immuable ?
Un objet immuable garantit la sécurité des données en multithreading, évite les effets de bord et simplifie la maintenance en interdisant les modifications après sa création.