Les modèles de conception jouent un rôle crucial dans le domaine du génie logiciel, offrant des solutions élégantes et testées pour des problèmes récurrents. Dans cet article, nous explorerons quelques-uns des modèles fondamentaux, en mettant l'accent sur leur utilité et leurs implémentations pratiques.
1. Modèle de Conception Créationnel - Le Modèle Singleton
Le Modèle Singleton vise à créer une seule instance d'une classe et à fournir un unique point d'accès global à cet objet. Une implémentation courante se trouve dans la classe Calendar
de Java, où vous ne pouvez pas créer une instance directe. L'accès se fait via la méthode getInstance()
. Nous examinerons les méthodes d'instanciation suivantes :
Instanciation Immédiate
public class SingletonInstance {
private static SingletonInstance instance = new SingletonInstance();
private SingletonInstance() {}
public static SingletonInstance getInstance() {
return instance;
}
}
Instanciation Paresseuse
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
Instanciation Thread-Safe
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance = null;
private ThreadSafeSingleton() {}
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}
Instanciation Thread-Safe avec Double Vérification
public class ThreadSafeSingletonDoubleLocking {
private static ThreadSafeSingletonDoubleLocking instance = null;
private ThreadSafeSingletonDoubleLocking() {}
public static ThreadSafeSingletonDoubleLocking getInstance() {
if (instance == null) {
synchronized (ThreadSafeSingletonDoubleLocking.class) {
if (instance == null) {
instance = new ThreadSafeSingletonDoubleLocking();
}
}
}
return instance;
}
}
2. Modèle de Conception Structurel - Le Modèle Décorateur
Le Modèle Décorateur permet de modifier la fonctionnalité d'un objet à l'exécution. Illustrons cela avec un exemple de café. Imaginons un café avec différentes saveurs de base, auquel on peut ajouter des extras tels que le sucre ou le lait. Plutôt que de créer des sous-classes infinies pour chaque combinaison, le modèle décorateur offre une solution plus élégante :
Classe Abstraite Beverage
public abstract class Boisson {
private String description;
public Boisson(String description) {
super();
this.description = description;
}
public String getDescription() {
return description;
}
public abstract double cout();
}
Classes de Boissons Concrètes
public class MelangeMaisons extends Boisson {
public MelangeMaisons() {
super("Mélange maison");
}
@Override
public double cout() {
return 250;
}
}
public class Moka extends Decorateur {
public Moka(Boisson boisson) {
super("Moka", boisson);
}
@Override
public String getDescription() {
return boisson.getDescription() + " avec Moka";
}
@Override
public double cout() {
return boisson.cout() + 50;
}
}
public class Lait extends Decorateur {
public Lait(Boisson boisson) {
super("Lait", boisson);
}
@Override
public String getDescription() {
return boisson.getDescription() + " avec Lait";
}
@Override
public double cout() {
return boisson.cout() + 100;
}
3. Modèle de Conception Comportemental - Le Modèle Commande
Le Modèle Commande se concentre sur la communication entre classes et objets, visant à réduire le couplage. Dans un scénario de restaurant, cela se traduit par une commande passée au serveur (invocateur), qui la transmet au chef (récepteur) pour exécution. Voici une mise en œuvre basique :
Interface Commande
public interface Commande {
void executer();
}
Commande Concrète
public class Commander implements Commande {
private Chef chef;
private String plat;
public Commander(Chef chef, String plat) {
this.chef = chef;
this.plat = plat;
}
@Override
public void executer() {
if (plat.equals("Pâtes")) {
chef.cuisinerPates();
} else {
chef.cuireGateau();
}
}
}
Invocateur
public class Serveur {
private Commande commande;
public Serveur(Commande commande) {
this.commande = commande;
}
public void executerCommande() {
commande.executer();
}
}
Récepteur
public class Chef {
public void cuisinerPates() {
System.out.println("Le chef cuisine des pâtes Alfredo...");
}
public void cuireGateau() {
System.out.println("Le chef prépare un gâteau au chocolat fondant...");
}
}
Client
public class Client {
public static void main(String[] args) {
Chef chef = new Chef();
Commande commande = new Commander(chef, "Pâtes");
Serveur serveur = new Serveur(commande);
serveur.executerCommande();
commande = new Commander(chef, "Gâteau");
serveur = new Serveur(commande);
serveur.executerCommande();
}
}
En conclusion, les modèles de conception offrent des solutions puissantes et éprouvées pour des problèmes courants. Que ce soit pour garantir une seule instance d'une classe, ajouter dynamiquement des fonctionnalités à un objet, ou définir des interactions flexibles entre classes, ces modèles constituent des outils essentiels pour tout développeur. En les comprenant et en les appliquant judicieusement, vous pouvez améliorer la structure et la flexibilité de votre code, tout en évitant les pièges courants du développement logiciel.