Modèles de conception en C# pour avancés : de la théorie au code
\n\nLes modèles de conception ne sont pas simplement des concepts abstraits issus des manuels. Pour un développeur C# expérimenté, ils deviennent une boîte à outils permettant de créer une architecture d'applications flexible, extensible et maintenable. Dans cet article, nous allons analyser trois modèles puissants qui restent souvent en dehors des cours de base : Visitor, Strategy et Command. Vous verrez non seulement leur implémentation en C#, mais vous comprendrez aussi comment ils résolvent des problèmes réels dans l'écosystème .NET.
\n\n1. Patron Visitor (Visiteur) — travailler avec une hiérarchie d'objets
\n\nLe patron Visitor permet d'ajouter de nouvelles opérations à des classes existantes sans modifier leur structure. Cela est particulièrement utile lorsque vous avez une hiérarchie d'objets complexe et que vous souhaitez effectuer différentes actions en fonction de leur type.
\n\nProblème
\nImaginez que vous ayez un système de gestion documentaire avec les classes TextDocument, Spreadsheet et Presentation. Vous devez ajouter une fonctionnalité d'exportation en PDF, HTML et XML. Sans le patron Visitor, vous devriez modifier chaque classe, ce qui viole le principe d'ouverture/fermeture (OCP).
Implémentation en C#
\n// 1. Interface de l'élément\npublic interface IElement\n{\n void Accept(IVisitor visitor);\n}\n\n// 2. Éléments concrets\npublic class TextDocument : IElement\n{\n public string Content { get; set; }\n public void Accept(IVisitor visitor) => visitor.Visit(this);\n}\n\npublic class Spreadsheet : IElement\n{\n public List<List<string>> Rows { get; set; }\n public void Accept(IVisitor visitor) => visitor.Visit(this);\n}\n\n// 3. Interface du visiteur\npublic interface IVisitor\n{\n void Visit(TextDocument doc);\n void Visit(Spreadsheet sheet);\n}\n\n// 4. Visiteur concret — exportation en HTML\npublic class HtmlExportVisitor : IVisitor\n{\n public void Visit(TextDocument doc)\n {\n Console.WriteLine($"<h1>{doc.Content}</h1>");\n }\n\n public void Visit(Spreadsheet sheet)\n {\n Console.WriteLine("<table>");\n foreach (var row in sheet.Rows)\n {\n Console.WriteLine("<tr><td>" + string.Join("</td><td>", row) + "</td></tr>");\n }\n Console.WriteLine("</table>");\n }\n}\n\n// Utilisation\nvar docs = new List<IElement> { new TextDocument { Content = "Bonjour" }, new Spreadsheet { Rows = new List<List<string>> { new List<string> { "A", "B" } } } };\nvar visitor = new HtmlExportVisitor();\nforeach (var doc in docs) doc.Accept(visitor);\n\nAvantages : facile d'ajouter de nouvelles opérations sans toucher aux classes existantes. Inconvénients : si la hiérarchie des éléments change fréquemment, Visitor devient peu pratique.
\n\n2. Patron Strategy (Stratégie) — changement dynamique d'algorithmes
\n\nLe patron Strategy permet de définir une famille d'algorithmes, d'encapsuler chacun d'eux et de les rendre interchangeables. C'est une façon classique d'éviter les énormes constructions if-else ou switch.
Problème
\nVous devez implémenter un système de calcul des frais de livraison. Les modes de livraison sont différents : par coursier, par la poste, retrait en magasin. Chaque mode a sa propre logique de calcul du prix.
\n\nImplémentation en C#
\n// 1. Interface de la stratégie\npublic interface IShippingStrategy\n{\n decimal CalculateCost(Order order);\n}\n\n// 2. Stratégies concrètes\npublic class CourierShipping : IShippingStrategy\n{\n public decimal CalculateCost(Order order) => order.Weight * 5.0m + 100;\n}\n\npublic class PostShipping : IShippingStrategy\n{\n public decimal CalculateCost(Order order) => order.Weight * 2.0m + 50;\n}\n\npublic class PickupShipping : IShippingStrategy\n{\n public decimal CalculateCost(Order