Refatoração de código legado em Java: do A ao Z
Trabalhar com código herdado (legado) em Java é um desafio que todo desenvolvedor enfrenta. Código antigo geralmente carece de testes, contém padrões obsoletos e é difícil de modificar. No entanto, reescrever todo o projeto nem sempre é a melhor solução. Neste artigo, vamos abordar uma abordagem comprovada para refatorar código legado: desde a análise até a implementação das mudanças, com exemplos concretos em Java.
1. Diagnóstico e preparação: antes de tocar no código
O primeiro e mais importante passo é entender com o que você está lidando. Não comece a refatoração com edições caóticas de linhas. Em vez disso:
- Mapa de dependências: visualize quais classes dependem de quais. Use ferramentas como IntelliJ IDEA Dependency Viewer ou JDepend.
- Testes de caracterização (Characterization Tests): escreva testes que capturem o comportamento atual do código. Esta é sua rede de segurança.
- Identifique pontos de entrada: determine quais métodos são chamados com mais frequência e onde ocorrem as principais alterações.
Exemplo de um teste de caracterização simples para um método que precisa ser refatorado:
import org.junit.jupiter.api.Test;import static org.junit.jupiter.api.Assertions.assertEquals;
public class LegacyCalculatorTest {
@Test public void testLegacyCalculate() { LegacyCalculator calc = new LegacyCalculator(); // Capturando o comportamento atual int result = calc.calculate(10, 5, "add"); assertEquals(15, result, "Comportamento atual: adição"); result = calc.calculate(10, 5, "sub"); assertEquals(5, result, "Comportamento atual: subtração"); }}Esse teste não verifica a correção da lógica em sentido absoluto — ele apenas captura o que o código faz atualmente. Após a refatoração, o teste deve passar; caso contrário, você alterou o comportamento.
2. Estratégia de refatoração "cirúrgica": método por método
Tentar refatorar uma classe inteira de uma só vez é uma maneira infalível de introduzir bugs. Use a abordagem "Arqueologia de Combate" (Combat Archaeology):
- Escolha um método ou um pequeno grupo de métodos relacionados.
- Escreva testes de caracterização para eles (se não existirem).
- Aplique uma técnica de refatoração (por exemplo, Extract Method ou Rename Variable).
- Execute os testes. Eles devem passar.
- Faça o commit das alterações com uma descrição clara.
Exemplo: refatoração de um método com "números mágicos" e uma longa lista de parâmetros.
Código original (ruim):
public class OrderProcessor { public double process(double amount, int type, double discount) { double result; if (type == 1) { result = amount * 1.2; // 20% de acréscimo if (discount > 0.1) { result = result - result * 0.05; // 5% de desconto } } else if (type == 2) { result = amount * 1.5; // 50% de acréscimo } else { result = amount; } return result; }}Após a refatoração (melhorado):
public class OrderProcessor { private static final double PREMIUM_MARKUP = 1.2; private static final double STANDARD_MARKUP = 1.5; private static final double LOYALTY_DISCOUNT = 0.05; private static final double DISCOUNT_THRESHOLD = 0.1;
public double process(OrderType type, double amount, double discount) { double basePrice = applyMarkup(amount, type); return applyLoyaltyDiscount(basePrice, discount); }
private double applyMarkup(double amount, OrderType type) { return switch (type) { case PREMIUM -> amount * PREMIUM_MARKUP; case STANDARD -> amount * STANDARD_MARKUP; case BASIC