Refactorización de código legacy en C#: guía paso a paso

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: Por qué la refactorización de código legacy en C# es una necesidad



Cada desarrollador .NET tarde o temprano se enfrenta a código legacy. No tiene que ser un proyecto de 20 años en .NET Framework 1.1 — a menudo se vuelve "legacy" el código escrito hace un año con prisas, sin pruebas y violando los principios SOLID. Trabajar con dicho código se convierte en una pesadilla: cualquier cambio rompe la funcionalidad en lugares inesperados, y entender la lógica solo es posible mediante depuración.



La refactorización no es reescribir desde cero. Es mejorar la estructura interna del código sin cambiar su comportamiento externo. En este artículo analizaremos técnicas prácticas de refactorización de código legacy en C# que se pueden aplicar de inmediato, minimizando riesgos.



1. Preparación: La regla de oro de la refactorización



Antes de cambiar el código, asegúrese de tener una "red de seguridad" — un conjunto de pruebas automatizadas. Sin ellas, la refactorización se convierte en una adivinanza.



Creación de pruebas de caracterización

Si no hay pruebas, escriba pruebas de caracterización (también conocidas como golden master o snapshot tests). La idea es simple: ejecutamos el método con diferentes datos de entrada, registramos el resultado, y luego verificamos que después de la refactorización el resultado no haya cambiado.



// Ejemplo de prueba de caracterización para un método legacy[Test]public void CalculateTotal_ShouldReturnSameResult_AfterRefactoring(){    // Arrange    var order = new Order { Items = new List<Item> { new Item { Price = 100, Quantity = 2 } } };    var expected = 200; // Resultado conocido de antemano

// Act var result = LegacyOrderCalculator.CalculateTotal(order);

// Assert Assert.AreEqual(expected, result);}


Consejo: Use bibliotecas como Verify o ApprovalTests para pruebas de snapshot automáticas.



2. Técnica n.º 1: Extracción de método (Extract Method)



La técnica más simple y efectiva. Cuando ve un método de 200 líneas con comentarios como "// aquí cálculo de descuento" — es un candidato ideal.



Ejemplo: Método grande



public decimal ProcessOrder(Order order){    decimal total = 0;    foreach (var item in order.Items)    {        total += item.Price * item.Quantity;    }

// Cálculo de descuento if (order.Customer.IsPremium) { total *= 0.9m; // 10% de descuento } else if (order.TotalAmount > 1000) { total *= 0.95m; // 5% de descuento }

// Aplicación de impuesto total *= 1.2m; // 20% de impuesto

return total;}


Después de la refactorización



public decimal ProcessOrder(Order order){    decimal subtotal = CalculateSubtotal(order.Items);    decimal afterDiscount = ApplyDiscount(subtotal, order.Customer);    return ApplyTax(afterDiscount);}

private decimal CalculateSubtotal(IEnumerable<Item> items){ return items.Sum(item => item.Price * item.Quantity);}

private decimal ApplyDiscount(decimal total, Customer customer){ if (customer.IsPremium) return total * 0.9m; if (total > 1000) return total * 0.95m; return total;}

private decimal ApplyTax(decimal amount){ return amount * 1.2m;}


Ventajas:

  • Cada método tiene una única responsabilidad
  • El código se vuelve legible y comprobable
  • La lógica de descuento se puede reutilizar en otros lugares


3. Técnica n.º 2: Reemplazo de lógica condicional por polimorfismo



El código legacy a menudo contiene largas cadenas de if-else o switch. Esto viola el principio de abierto/cerrado (OCP).



Ejemplo: Switch por tipo de envío



public decimal CalculateShippingCost(Order order){    switch (order.ShippingType)    {        case "Standard":            return 5;        case "Express":            return 15;        case "Overnight":

Blogs

Book Recommendations