Refatoração de código legado em C#: do A ao Z
Trabalhar com código legado é uma realidade inevitável para qualquer desenvolvedor .NET. Projetos antigos em C# frequentemente contêm "código espaguete", números mágicos, classes enormes e falta de testes. Em vez de reescrever tudo do zero (o que é caro e arriscado), uma refatoração bem-feita permite melhorar o código gradualmente, reduzindo a dívida técnica. Neste artigo, vamos analisar uma estratégia passo a passo para refatorar código legado em C# com exemplos concretos.
1. Preparação para a refatoração: segurança em primeiro lugar
A regra principal da refatoração de código legado é não quebrar o que funciona. Sem testes, qualquer alteração pode causar bugs em produção. Portanto, o primeiro passo é criar uma "rede de segurança".
1.1. Adicionando testes (Testes de Caracterização)
Se não houver testes, eles precisam ser escritos. Use a abordagem de Testes de Caracterização. A ideia: você executa o código existente com diferentes entradas e registra o comportamento atual como referência.
// Exemplo para um método legadopublic class LegacyCalculator{ public int CalculateTotal(int a, int b) { // Código legado que não entendemos completamente return a + b * 2; // Lógica suspeita }}
// Teste de caracterização[Test]public void CalculateTotal_WhenCalled_ReturnsCurrentBehavior(){ var calc = new LegacyCalculator(); int result = calc.CalculateTotal(3, 4); // Registramos o resultado atual como esperado Assert.AreEqual(11, result); // 3 + 4*2 = 11}Após escrever esses testes, você pode refatorar com segurança, sabendo que os testes detectarão regressões.
1.2. Isolamento de dependências
Código legado frequentemente é fortemente acoplado a banco de dados, sistema de arquivos ou serviços externos. Use Extrair Interface e injeção de dependência (DI) para isolar.
// Antes: dependência rígidapublic class OrderProcessor{ private readonly SqlDatabase _db = new SqlDatabase(); public void Process(Order order) { _db.Save(order); }}
// Depois: interface e DIpublic interface IOrderRepository{ void Save(Order order);}
public class OrderProcessor{ private readonly IOrderRepository _repository; public OrderProcessor(IOrderRepository repository) { _repository = repository; } public void Process(Order order) { _repository.Save(order); }}2. Principais técnicas de refatoração de código legado em C#
Após a preparação, podemos começar a melhorar o código. Aqui estão as principais técnicas que funcionam em projetos .NET.
2.1. Extrair Método
Métodos enormes com mais de 200 linhas são clássicos em código legado. Divida-os em métodos menores e legíveis.
// Antes da refatoraçãopublic void ProcessInvoice(Invoice invoice){ // 50 linhas de cálculo de desconto decimal discount = 0; if (invoice.Total > 1000) discount = 0.1m; else if (invoice.Total > 500) discount = 0.05m; // ... muito mais código // 30 linhas de envio de email // ... }
// Depois da refatoraçãopublic void ProcessInvoice(Invoice invoice){ decimal discount = CalculateDiscount(invoice.Total); SendInvoiceEmail(invoice);}
private decimal CalculateDiscount(decimal total){ if (total > 1000) return 0.1m; if (total > 500) return 0.05m; return 0;}
private void SendInvoiceEmail(Invoice invoice){ // Lógica de envio}2.2. Substituir Números Mágicos por Constantes
Números mágicos tornam o código confuso. Substitua-os por constantes.
// Ruimif (user.Role == 3) { /* admin */ }
// Bompublic enum UserRole{ Guest = 1, User = 2, Admin = 3}
if (user.Role == (int)UserRole.Admin) { /* admin */ }