17. Anti-patrones relacionados con la violación de SOLID

Los anti-patrones son soluciones aparentemente convenientes que terminan generando problemas a largo plazo. En este capítulo revisamos conductas muy comunes que violan los principios SOLID, con ejemplos en Java y recomendaciones para darles la vuelta.

17.1 God Object (violación de SRP)

Un “objeto dios” centraliza múltiples responsabilidades: reglas de negocio, acceso a datos, validaciones y control de flujo.

class SistemaService {
    void procesar(Pedido pedido) {
        validar(pedido);
        guardar(pedido);
        enviarEmail(pedido);
        generarReporte(pedido);
    }
    // docenas de métodos privados...
}
  • Riesgos: cambios frecuentes, dificultad para testear, alto acoplamiento.
  • Corrección: aplicar SRP, extraer servicios especializados, introducir interfaces.

17.2 Shotgun Surgery (violación de SRP y OCP)

Cuando agregar una funcionalidad obliga a modificar múltiples archivos no relacionados hablamos de “cirugía de escopeta”. A menudo se debe a la falta de un punto único de extensión.

  • Riesgos: gran probabilidad de regresiones, tiempos de entrega elevados.
  • Corrección: concentrar las variaciones en componentes extensibles, usar fábricas o estrategias.

17.3 Switch Envy (violación de OCP)

Es habitual ver estructuras switch o cadenas de if que revisan tipos o constantes para decidir el comportamiento.

class CalculadoraBonos {
    BigDecimal calcular(String categoria, BigDecimal salario) {
        switch (categoria) {
            case "A": return salario.multiply(new BigDecimal("0.1"));
            case "B": return salario.multiply(new BigDecimal("0.15"));
            case "C": return salario.multiply(new BigDecimal("0.2"));
            default: return BigDecimal.ZERO;
        }
    }
}
  • Riesgos: cada nueva categoría obliga a editar el método; las reglas se dispersan.
  • Corrección: definir una interfaz y desacoplar las variantes (Strategy, OCP + DIP).

17.4 Type Checking Mania (violación de LSP)

El código que pregunta constantemente por la clase concreta de un objeto indica que la jerarquía no respeta LSP.

void procesar(Notificacion notificacion) {
    if (notificacion instanceof NotificacionEmail) {
        enviarEmail((NotificacionEmail) notificacion);
    } else if (notificacion instanceof NotificacionSms) {
        enviarSms((NotificacionSms) notificacion);
    } else {
        throw new IllegalArgumentException("Tipo no soportado");
    }
}
  • Riesgos: contratos inconsistentes, duplicación de lógica.
  • Corrección: usar polimorfismo, garantizar que cada subtipo cumpla el contrato y delegar el comportamiento.

17.5 Interface Pollution (violación de ISP)

Una interfaz que agrupa operaciones ajenas entre sí obliga a los implementadores a depender de métodos que no necesitan.

interface ServicioGeneral {
    void crear();
    void actualizar();
    void imprimir();
    void exportar();
}
  • Riesgos: implementación con métodos vacíos, dificultad para testear, acoplamiento innecesario.
  • Corrección: dividir la interfaz según las capacidades reales, aplicar ISP.

17.6 Dependency Glue (violación de DIP)

Ocurre cuando se crean dependencias concretas dentro de clases de alto nivel, impidiendo la inversión real.

class ReporteDomainService {
    void generar() {
        ExportadorPdf exportador = new ExportadorPdf();
        exportador.exportar();
    }
}
  • Riesgos: poca flexibilidad, pruebas dependientes de recursos externos.
  • Corrección: declarar una interfaz (Exportador) e inyectar la implementación por constructor o contenedor IoC.

17.7 Big Ball of Mud

Este anti-patrón describe un sistema sin estructura reconocible. La falta de límites y contratos claros dificulta la aplicación de cualquier principio SOLID.

  • Riesgos: curva de aprendizaje elevada, acoplamientos cíclicos, deuda técnica infinita.
  • Corrección: identificar bounded contexts, modularizar, aplicar refactorizaciones graduales siguiendo SOLID.

17.8 Lava Flow

El “flujo de lava” son fragmentos de código legacy que permanecen por miedo a tocarlos. Muchos incumplen SOLID, pero nadie se anima a refactorizarlos.

  • Riesgos: bloquea mejoras, genera bugs recurrentes.
  • Corrección: crear pruebas de caracterización, aplicar refactorizaciones planificadas y documentar las decisiones.

17.9 Shotgun Debugging

Modificar tres o cuatro lugares al azar para “ver si se arregla” indica falta de comprensión del diseño. Suele ocurrir cuando las dependencias no están invertidas o las interfaces son confusas.

  • Riesgos: regresiones, tiempo desperdiciado, pérdida de confianza en el equipo.
  • Corrección: analizar el flujo, identificar contratos, escribir tests que reproduzcan el error y refactorizar.

17.10 Checklist para detectar anti-patrones

  • ¿Existen clases con parámetros o métodos no utilizados?
  • ¿Se repiten estructuras condicionales que dependen de tipos?
  • ¿Hay interfaces que obligan a implementar métodos vacíos?
  • ¿Las dependencias se instancian directamente dentro de los servicios?
  • ¿Los cambios menores requieren tocar varios módulos?

Detectar estos anti-patrones tempranamente permite aplicar SOLID antes de que el sistema se vuelva incontrolable. En el próximo tema exploraremos herramientas y técnicas para detectar incumplimientos de los principios de forma automatizada.