Princípio DRY: O Guia Prático para Eliminar a Repetição de Código Sem Cair na Armadilha da Abstração Prematura
Repetir linhas de código idênticas em vários arquivos é o primeiro sinal de que um sistema vai se tornar difícil de manter. O princípio DRY (Don't Repeat Yourself) surge justamente para combater essa redundância. Neste post, vamos entender o verdadeiro significado do DRY, ver exemplos práticos de refatoração no ecossistema Java moderno e aprender a traçar a linha tênue entre um código reaproveitável e uma arquitetura hipercomplexa e engessada.
O Pesadelo do "Esqueci de alterar no outro arquivo"
Imagine o cenário: você precisa validar o formato do CPF de um cliente. Você escreve uma lógica Regex na classe de cadastro do usuário. Meses depois, uma nova funcionalidade de emissão de notas fiscais precisa da mesma validação. Com pressa para entregar a tarefa, você faz o clássico Ctrl+C e Ctrl+V daquela lógica para dentro do novo serviço.
O sistema funciona perfeitamente. Até que a regra muda ou um bug é encontrado no Regex.
Agora, você precisa lembrar de alterar a validação no cadastro, na nota fiscal e em qualquer outro lugar que tenha copiado o código. Se esquecer de um único arquivo, o sistema entra em um estado inconsistente. Esse é o custo invisível da repetição de código.
O que é o Princípio DRY?
DRY é o acrônimo para "Don't Repeat Yourself" (Não se repita). A definição original do livro O Programador Pragmático diz o seguinte:
"Toda peça de conhecimento deve ter uma representação única, inequívoca e definitiva dentro de um sistema."
O ponto central aqui é a palavra conhecimento. O DRY não dita apenas que você não deve ter linhas de código textualmente idênticas. Ele diz que uma regra de negócio ou uma lógica de sistema não deve estar duplicada. Se você precisar alterar o comportamento de uma regra, essa alteração deve ser feita em apenas um único lugar.
DRY na Prática com Java
Vamos a um cenário muito comum no ecossistema Java de desenvolvimento corporativo: regras de cálculo de taxas ou validações em camadas de serviço diferentes.
Sem DRY (A armadilha da cópia)
Imagine que temos dois serviços diferentes na nossa aplicação que precisam calcular o desconto padrão de 10% para compras acima de um determinado valor.
@Service
public class PedidoService {
public BigDecimal fecharPedido(BigDecimal total) {
// Lógica duplicada
if (total.compareTo(new BigDecimal("100.00")) > 0) {
total = total.multiply(new BigDecimal("0.90"));
}
return total;
}
}
@Service
public class OrcamentoService {
public BigDecimal emitirOrcamento(BigDecimal total) {
// A mesma lógica duplicada
if (total.compareTo(new BigDecimal("100.00")) > 0) {
total = total.multiply(new BigDecimal("0.90"));
}
return total;
}
}
Se amanhã o desconto mudar para 12% ou o valor mínimo para R$ 150,00, temos duas classes para rastrear e alterar.
Aplicando o DRY (Centralizando o Conhecimento)
Podemos isolar essa regra de negócio em um componente dedicado ou na própria entidade de domínio de forma rica, garantindo uma única fonte da verdade.
@Component
public class CalculadoraDesconto {
private static final BigDecimal VALOR_MINIMO = new BigDecimal("100.00");
private static final BigDecimal FATOR_DESCONTO = new BigDecimal("0.90");
public BigDecimal aplicarDescontoPadrao(BigDecimal total) {
if (total.compareTo(VALOR_MINIMO) > 0) {
return total.multiply(FATOR_DESCONTO);
}
return total;
}
}
Agora, basta injetar a CalculadoraDesconto via construtor tanto no PedidoService quanto no OrcamentoService. Se a regra de negócio mudar, alteramos o código em um único arquivo, e todo o ecossistema se atualiza de forma segura.
A Armadilha Oculta: O Perigo do "Over-DRY" (Abstração Prematura)
Como desenvolvedores, quando descobrimos o DRY, desenvolvemos uma espécie de obsessão. Não suportamos ver duas linhas parecidas que já queremos criar uma classe utilitária, uma herança complexa ou uma interface genérica para unificá-las.
Cuidado: isso pode destruir a legibilidade do seu código. Existe um princípio chamado WET (Write Everything Twice ou We Enjoy Typing), que brinca com o oposto do DRY e traz um conselho pragmático: Duplicação é mais barata do que a abstração errada.
Se duas partes do código parecem idênticas textualmente, mas representam conceitos de negócio totalmente diferentes, elas não devem ser unificadas.
O Exemplo Clássico do Erro do DRY:
Imagine que a regra de validação do tamanho do nome de um Produto é de no mínimo 3 caracteres. Coincidentemente, o nome de um CupomDeDesconto também exige no mínimo 3 caracteres.
Se você unificar essas duas validações em um único método genérico ValidarTextoMinimo3(), você acabou de acoplar o Produto ao Cupom. Se amanhã o time de negócio decidir que o Cupom pode ter 2 caracteres, ao alterar a função genérica, você quebrará a validação de Produtos por acidente.
Regra de ouro: Só aplique o DRY se a alteração futura daquela regra de negócio obrigatoriamente exigir a alteração em ambos os lugares. Se elas mudam por motivos diferentes, a duplicação textual é pura coincidência. Mantenha-as separadas.
Conclusão
Aplicar o princípio DRY de forma inteligente é o que garante a evolução saudável de qualquer software. Ele reduz o esforço de manutenção, elimina bugs de inconsistência e deixa a sua base de código muito mais enxuta. No entanto, a maturidade técnica está em saber quando o código é conceitualmente repetido ou se são apenas duas regras distintas que compartilham a mesma "aparência" por acaso.
Antes de isolar uma lógica, pergunte-se: "Se essa regra de negócio mudar, esses dois arquivos precisam mudar juntos?". Se a resposta for sim, aplique o DRY sem pensar duas vezes.