Modèles de conception en C# avec exemples : de A à Z
Les modèles de conception (design patterns) sont des solutions éprouvées aux problèmes typiques rencontrés lors du développement de logiciels. Ils ne constituent pas un code prêt à l'emploi, mais représentent des patrons que l'on peut adapter à une situation spécifique. Pour un développeur C#, la connaissance des modèles n'est pas seulement un atout sur un CV, mais une nécessité pour créer une architecture flexible, maintenable et évolutive.
Dans cet article, nous allons analyser les trois principaux groupes de modèles selon la classification GoF (Gang of Four) : les patrons de création, structurels et comportementaux. Chaque modèle sera examiné avec un exemple pratique en C# (.NET).
Patrons de création (Creational Patterns)
Ces patrons gèrent le processus de création des objets, rendant le système indépendant de la manière dont ils sont créés. Examinons les deux plus populaires.
Singleton (Singleton)
Garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès global à celle-ci. En C#, pour un fonctionnement sécurisé dans un environnement multithread, on utilise Lazy<T>.
public sealed class Logger{ private static readonly Lazy<Logger> _instance = new Lazy<Logger>(() => new Logger());
public static Logger Instance => _instance.Value;
private Logger() { }
public void Log(string message) { Console.WriteLine($"[{DateTime.Now}] {message}"); }}
// UtilisationLogger.Instance.Log("Application démarrée");Méthode de fabrique (Factory Method)
Définit une interface pour créer un objet, mais permet aux sous-classes de modifier le type d'objet créé. Idéal pour les situations où le type d'objet n'est pas connu à l'avance.
public interface ITransport{ void Deliver();}
public class Truck : ITransport{ public void Deliver() => Console.WriteLine("Livraison par voie terrestre");}
public class Ship : ITransport{ public void Deliver() => Console.WriteLine("Livraison par voie maritime");}
public abstract class Logistics{ public abstract ITransport CreateTransport();
public void PlanDelivery() { var transport = CreateTransport(); transport.Deliver(); }}
public class RoadLogistics : Logistics{ public override ITransport CreateTransport() => new Truck();}
// UtilisationLogistics logistics = new RoadLogistics();logistics.PlanDelivery(); // Sortie : Livraison par voie terrestrePatrons structurels (Structural Patterns)
Ils définissent les moyens de construire des relations entre les objets, assurant flexibilité et efficacité de l'architecture.
Adaptateur (Adapter)
Permet à des objets avec des interfaces incompatibles de fonctionner ensemble. Souvent utilisé lors de l'intégration avec du code ancien ou des bibliothèques tierces.
// Interface cible attendue par le clientpublic interface IModernPrinter{ void Print(string text);}
// Ancienne classe avec une interface incompatiblepublic class OldPrinter{ public void PrintOldStyle(string message) { Console.WriteLine($"Ancienne imprimante : {message}"); }}
// Adaptateurpublic class PrinterAdapter : IModernPrinter{ private readonly OldPrinter _oldPrinter;
public PrinterAdapter(OldPrinter oldPrinter) { _oldPrinter = oldPrinter; }
public void Print(string text) { _oldPrinter.PrintOldStyle(text); }}
// UtilisationIModernPrinter printer = new PrinterAdapter(new OldPrinter());printer.Print("Bonjour, le monde !"); // Sortie : Ancienne imprimante : Bonjour, le monde !Décorateur (Decorator)
Ajoute dynamiquement de nouvelles responsabilités à un objet sans modifier son code. C'est une alternative flexible à l'héritage.
public interface ICoffee{ double GetCost(); string GetDescription();}
public class SimpleCoffee : ICoffee{