Refatoração de código legado em Java: um guia passo a passo
Trabalhar com código legado em Java é uma das tarefas mais frequentes e desafiadoras que os desenvolvedores enfrentam. Muitas vezes, esse código não possui testes, é escrito em estilo monolítico e contém vários antipadrões. No entanto, reescrever completamente o sistema é caro e arriscado. É muito mais eficaz aplicar uma refatoração metódica. Neste artigo, vamos analisar uma abordagem prática para refatorar código Java legado, que o tornará mais limpo, seguro e sustentável.
1. Preparação: análise e criação de uma "rede de segurança"
Antes de alterar o código, é necessário entender o que ele faz e como não quebrar a lógica existente. O princípio principal da refatoração de código legado é não alterar o comportamento. Para isso, precisamos criar uma "rede de segurança" — um conjunto de testes.
Comece estudando o código. Encontre os pontos de entrada (métodos que são chamados externamente) e tente determinar os resultados esperados. Mesmo que o código esteja mal escrito, ele faz algo corretamente.
Após a análise, escreva testes de caracterização (Characterization Tests). Esses testes registram o comportamento atual do código. Eles não verificam se a lógica está correta, apenas documentam o que está acontecendo no momento.
Exemplo: temos uma classe monstro que processa pedidos.
public class OrderProcessor { public void process(Order order) { // 200 linhas de código espaguete if (order.getTotal() > 100) { order.setStatus("VIP"); } else { order.setStatus("STANDARD"); } // ... muita outra lógica }}Não sabemos o que está dentro, mas podemos escrever um teste:
@Testpublic void testProcessOrderWithHighTotal() { // Given Order order = new Order(); order.setTotal(150); OrderProcessor processor = new OrderProcessor();
// When processor.process(order);
// Then (registramos o comportamento atual) assertEquals("VIP", order.getStatus());}Execute os testes. Se eles passarem, você registrou o comportamento atual. Agora você tem proteção contra regressão.
2. Extrair método (Extract Method)
A refatoração mais segura e eficaz para métodos grandes. A ideia é simples: pegue um fragmento de código que faz uma coisa específica e extraia-o para um método separado com um nome claro.
Vamos considerar um exemplo de código ruim:
public double calculateEmployeePay(Employee emp) { double basePay = emp.getBasePay(); double overtimePay = 0; if (emp.getHoursWorked() > 40) { overtimePay = (emp.getHoursWorked() - 40) * basePay * 1.5; } double tax = basePay * 0.13; double insurance = 5000; return basePay + overtimePay - tax - insurance;}Aqui, o cálculo de horas extras, impostos e seguro está misturado. Vamos separá-los:
public double calculateEmployeePay(Employee emp) { double basePay = emp.getBasePay(); double overtimePay = calculateOvertimePay(emp, basePay); double tax = calculateTax(basePay); double insurance = getInsuranceCost(); return basePay + overtimePay - tax - insurance;}
private double calculateOvertimePay(Employee emp, double basePay) { if (emp.getHoursWorked() > 40) { return (emp.getHoursWorked() - 40) * basePay * 1.5; } return 0;}
private double calculateTax(double basePay) { return basePay * 0.13;}
private double getInsuranceCost() { return 5000;}Agora cada método é responsável por uma única tarefa. O código ficou mais legível e é mais fácil testá-lo individualmente.
3. Remoção de duplicação (DRY)
O código legado frequentemente sofre de cópia e colagem. O mesmo trecho de lógica pode aparecer em classes diferentes. Isso é perigoso: ao alterar os requisitos, você pode esquecer de corrigir uma das duplicatas.
Suponha que em dois serviços diferentes
```