Patrones de diseño en C# para avanzados: de la teoría al código
\n\nLos patrones de diseño no son solo conceptos abstractos de los libros de texto. Para un desarrollador experimentado en C#, se convierten en un conjunto de herramientas que permiten crear una arquitectura de aplicaciones flexible, extensible y mantenible. En este artículo, analizaremos tres patrones potentes que a menudo quedan fuera de los cursos básicos: Visitor, Strategy y Command. No solo verás su implementación en C#, sino que también comprenderás cómo resuelven problemas reales en el ecosistema .NET.
\n\n1. Patrón Visitor (Visitante) — trabajo con jerarquías de objetos
\n\nEl patrón Visitor permite agregar nuevas operaciones a clases existentes sin modificar su estructura. Esto es especialmente útil cuando tienes una jerarquía compleja de objetos y deseas realizar diferentes acciones según su tipo.
\n\nProblema
\nImagina que tienes un sistema de gestión de documentos con las clases TextDocument, Spreadsheet y Presentation. Necesitas agregar funcionalidad de exportación a PDF, HTML y XML. Sin el patrón Visitor, tendrías que modificar cada clase, lo que viola el principio de abierto/cerrado (OCP).
Implementación en C#
\n// 1. Interfaz del elemento\npublic interface IElement\n{\n void Accept(IVisitor visitor);\n}\n\n// 2. Elementos concretos\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. Interfaz del visitante\npublic interface IVisitor\n{\n void Visit(TextDocument doc);\n void Visit(Spreadsheet sheet);\n}\n\n// 4. Visitante concreto — exportación a 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// Uso\nvar docs = new List<IElement> { new TextDocument { Content = "Hola" }, 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\nVentajas: es fácil agregar nuevas operaciones sin tocar las clases existentes. Desventajas: si la jerarquía de elementos cambia con frecuencia, Visitor se vuelve incómodo.
\n\n2. Patrón Strategy (Estrategia) — cambio dinámico de algoritmos
\n\nEl patrón Strategy permite definir una familia de algoritmos, encapsular cada uno de ellos y hacerlos intercambiables. Esta es una forma clásica de evitar enormes construcciones if-else o switch.
Problema
\nNecesitas implementar un sistema de cálculo de costos de envío. Los métodos de envío son diferentes: mensajería, correo postal, recogida en tienda. Cada método tiene su propia lógica de cálculo de precio.
\n\nImplementación en C#
\n// 1. Interfaz de estrategia\npublic interface IShippingStrategy\n{\n decimal CalculateCost(Order order);\n}\n\n// 2. Estrategias concretas\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