4. Principio DRY (Don't Repeat Yourself) - No te repitas

El principio DRY sostiene que cada pieza de conocimiento debe tener una representación única, inequívoca y autorizada dentro de un sistema. Su objetivo es evitar que la duplicación de lógica se convierta en una fuente continua de defectos y costos de mantenimiento.

Aplicar DRY no implica abstraer de manera excesiva, sino consolidar la información para que cada cambio se realice en un único lugar, manteniendo la coherencia del negocio.

4.1 Origen y motivación

DRY fue acuñado por Andrew Hunt y David Thomas en "The Pragmatic Programmer". Surgió como una respuesta a los problemas cotidianos provocados por la copia y el pegado indiscriminado de código y configuraciones.

Cuando dos fragmentos comparten la misma lógica, tarde o temprano uno de ellos queda desactualizado, generando defectos que son difíciles de detectar porque el programador asume que ambos fragmentos se encuentran sincronizados.

4.2 Tipos de duplicación frecuentes

En proyectos Java podemos encontrar duplicaciones de distinta naturaleza:

  • Duplicación literal: bloques de código iguales o casi iguales copiados en diferentes clases.
  • Duplicación semántica: lógicas que realizan la misma tarea con implementaciones levemente distintas.
  • Duplicación de configuraciones: propiedades repetidas en archivos `application.properties`, `pom.xml` o scripts de despliegue.
  • Duplicación de conocimiento: reglas de negocio replicadas en servicios, controladores y cliente web.

4.3 Cómo reconoce el equipo una duplicación

Un código duplicado suele generar conversaciones recurrentes durante revisiones de código: "este método ya existe en otra clase", "recuerdo haber implementado algo similar". También aparecen errores que solo se corrigen en una parte, dejando la otra obsoleta.

La automatización ayuda: algunas herramientas de análisis estático detectan duplicaciones textuales, pero el criterio profesional sigue siendo indispensable para evaluar duplicaciones más sutiles.

4.4 Duplicación aceptable vs. duplicación nociva

No todo lo repetido amerita una abstracción. En ocasiones, dos fragmentos similares representan etapas distintas del negocio y evolucionan por separado. DRY se aplica cuando la duplicación comparte el mismo motivo de cambio; si ambas partes necesitan modificarse al mismo tiempo, entonces la duplicación es nociva.

Aplicar una abstracción demasiado pronto puede introducir complejidad artificial. Conviene refactorizar cuando la duplicación ya es evidente y se confirma que la lógica representa un mismo concepto.

4.5 Estrategias para eliminar duplicaciones

Entre las técnicas más usadas encontramos:

  1. Extraer métodos o clases que encapsulen la lógica repetida.
  2. Utilizar patrones de diseño como Estrategia, Plantilla o Cadena de responsabilidad cuando la variación se centra en una pequeña parte del algoritmo.
  3. Generalizar constantes y configuraciones en archivos compartidos.
  4. Introducir tipos de valor que representen conceptos del dominio (por ejemplo, `Email`, `Dinero`).
  5. Compartir componentes front-end reutilizables cuando la duplicación se extiende a la interfaz.

4.6 DRY y bases de datos

El principio también se aplica a consultas SQL y mapeos. Vistas, procedimientos almacenados o repositorios reutilizables impiden que la misma consulta se defina en múltiples capas. En proyectos Java, frameworks como JPA permiten centralizar las consultas en una única entidad o repositorio.

4.7 Cobertura de pruebas como respaldo

Eliminar duplicaciones sin pruebas automatizadas es riesgoso. Un conjunto de pruebas bien diseñado permite refactorizar con confianza, asegurando que la nueva abstracción respeta el comportamiento original.

Antes de aplicar una transformación DRY, conviene escribir pruebas para el comportamiento duplicado, garantizando que ambas versiones se comportan igual.

4.8 Ejemplo práctico en Java: antes y después

Supongamos un servicio de facturación con dos métodos que calculan impuestos por separado, generando inconsistencias:

class FacturacionService {
    double calcularTotalFactura(Factura factura) {
        double subtotal = factura.subtotal();
        double iva = subtotal * 0.21;
        double ingresosBrutos = subtotal * 0.03;
        return subtotal + iva + ingresosBrutos;
    }

    double calcularTotalNotaCredito(NotaCredito nota) {
        double subtotal = nota.subtotal();
        double iva = subtotal * 0.21;
        double ingresosBrutos = subtotal * 0.027;
        return subtotal + iva + ingresosBrutos;
    }
}

El IVA debería ser el mismo en ambos casos, pero la copia modificada del código introduce un porcentaje distinto para ingresos brutos. Apliquemos DRY:

class CalculadoraDeImpuestos {
    private static final double IVA = 0.21;
    private static final double INGRESOS_BRUTOS = 0.03;

    double calcularImpuestos(double subtotal) {
        double iva = subtotal * IVA;
        double ingresosBrutos = subtotal * INGRESOS_BRUTOS;
        return iva + ingresosBrutos;
    }
}

class FacturacionService {
    private final CalculadoraDeImpuestos calculadora;

    FacturacionService(CalculadoraDeImpuestos calculadora) {
        this.calculadora = calculadora;
    }

    double calcularTotalFactura(Factura factura) {
        return factura.subtotal() + calculadora.calcularImpuestos(factura.subtotal());
    }

    double calcularTotalNotaCredito(NotaCredito nota) {
        return nota.subtotal() + calculadora.calcularImpuestos(nota.subtotal());
    }
}

Ahora la lógica tributaria reside en una clase exclusiva. Cualquier cambio impositivo se refleja en un solo lugar y los servicios reutilizan la misma implementación.

4.9 DRY más allá del código

El principio también se extiende a documentación, tableros de gestión y procesos operativos. Mantener una única fuente de verdad evita discrepancias entre manuales, diagramas y el sistema en ejecución.

4.10 Herramientas que ayudan

En el ecosistema Java existen soluciones que detectan duplicaciones o promueven la reutilización:

  • SonarQube: señala duplicaciones textuales y bloques sospechosos.
  • Checkstyle: permite definir reglas personalizadas para detectar prácticas indeseadas.
  • SpotBugs: analiza bytecode para encontrar patrones problemáticos.

Estas herramientas no sustituyen al criterio humano, pero actúan como redes de seguridad que alertan sobre duplicaciones accidentales.

4.11 Errores comunes al aplicar DRY

Algunos equipos confunden DRY con crear una mega abstracción que resuelva todos los casos, conduciendo a diseños crípticos. Otros se enfocan solo en reducir el número de líneas ignorando el significado del código.

La mejor práctica es abstraer con moderación, nombrar bien los componentes y validar que la nueva estructura representa un concepto claro del dominio.

4.12 Checklist para revisiones de código

Incorporar preguntas concretas en la revisión ayuda a sostener DRY:

  • ¿Existe algún servicio o clase que ya resuelva este problema?
  • ¿Estamos duplicando una constante o configuración?
  • ¿Es posible extraer un método o clase sin sacrificar claridad?
  • ¿Las pruebas automatizadas se mantuvieron al día para cubrir la nueva abstracción?

4.13 Resumen y próximos pasos

DRY es un pilar de la simplicidad porque concentra el conocimiento en ubicaciones bien definidas. Al adoptarlo, disminuye la probabilidad de defectos y se acelera el mantenimiento. En el siguiente tema aprenderemos a detectar duplicaciones de lógica de forma sistemática.