Modèles de conception avancés en C# : de la théorie à la pratique
Les modèles de conception (Design Patterns) sont des solutions éprouvées aux problèmes courants d'architecture logicielle. Au niveau débutant, les développeurs découvrent Singleton, Factory ou Adapter. Cependant, pour créer des systèmes flexibles, maintenables et évolutifs sur la plateforme .NET, il est nécessaire de maîtriser des modèles comportementaux et structurels plus complexes. Dans cet article, nous analyserons trois modèles avancés : Chain of Responsibility, State et Visitor. Chaque modèle sera illustré par un exemple pratique en C# utilisant les fonctionnalités modernes du langage (records, pattern matching, generics).
1. Chain of Responsibility (Chaîne de responsabilité)
Le modèle Chain of Responsibility permet de transmettre une requête séquentiellement le long d'une chaîne de gestionnaires, jusqu'à ce que l'un d'eux la traite. Cela évite un couplage rigide entre l'expéditeur et le destinataire. En C#, il est souvent utilisé pour le middleware dans ASP.NET Core ou pour la validation de données.
Exemple : système de vérification des droits d'accès
Supposons que nous ayons une requête pour exécuter une opération. Chaque gestionnaire vérifie une condition (authentification, autorisation, limites).
// Classe de base du gestionnairepublic abstract class Handler{ protected Handler? NextHandler;
public void SetNext(Handler handler) => NextHandler = handler;
public abstract void Handle(RequestContext context);}
public class AuthenticationHandler : Handler{ public override void Handle(RequestContext context) { if (context.IsAuthenticated) { Console.WriteLine("Authentification réussie."); NextHandler?.Handle(context); } else { Console.WriteLine("Erreur : utilisateur non authentifié."); } }}
public class AuthorizationHandler : Handler{ public override void Handle(RequestContext context) { if (context.Role == "Admin") { Console.WriteLine("Autorisation réussie."); NextHandler?.Handle(context); } else { Console.WriteLine("Erreur : droits insuffisants."); } }}
// Contexte de la requêtepublic record RequestContext(bool IsAuthenticated, string Role);
// Utilisationvar authHandler = new AuthenticationHandler();var authzHandler = new AuthorizationHandler();authHandler.SetNext(authzHandler);
var context = new RequestContext(true, "Admin");authHandler.Handle(context);// Sortie :// Authentification réussie.// Autorisation réussie.Ce modèle est idéal pour créer des pipelines de traitement, où chaque étape peut interrompre l'exécution ou transmettre le contrôle plus loin.
2. State (État)
Le modèle State permet à un objet de modifier son comportement lorsque son état interne change. Extérieurement, cela donne l'impression que l'objet change de classe. En C#, il est pratique à implémenter via des interfaces et la composition.
Exemple : gestion d'une commande dans une boutique en ligne
Une commande peut être dans les états : Nouvelle, Payée, Expédiée, Livrée. Chaque état définit les actions autorisées.
// Interface d'étatpublic interface IOrderState{ void Pay(Order order); void Ship(Order order); void Deliver(Order order);}
// États concretspublic class NewOrderState : IOrderState{ public void Pay(Order order) { Console.WriteLine("Commande payée."); order.State = new PaidOrderState(); }
public void Ship(Order order) => Console.WriteLine("Erreur : la commande n'est pas encore payée."); public void Deliver(Order order) => Console.WriteLine("Erreur : la commande n'est pas encore expédiée.");}
public class PaidOrderState : IOrde