Adoptar los principios SOLID aporta enormes beneficios, pero hacerlo de manera apresurada puede derivar en anti‑patrones que afectan la calidad. En esta sección reunimos los errores más frecuentes, su impacto y estrategias para prevenirlos.
Los ejemplos utilizan Java, pero los conceptos aplican a cualquier lenguaje orientado a objetos.
Un error habitual es introducir interfaces y capas adicionales sin una necesidad real. Esto genera código difícil de leer y tiempo extra de mantenimiento.
interface UsuarioValidadorStrategy {
boolean validar(Usuario usuario);
}
class UsuarioValidadorStrategyImpl implements UsuarioValidadorStrategy {
public boolean validar(Usuario usuario) {
return usuario.nombre() != null;
}
}
class UsuarioService {
private final UsuarioValidadorStrategy validador;
UsuarioService() {
this.validador = new UsuarioValidadorStrategyImpl();
}
}
Si la validación es simple y no va a cambiar, esta abstracción no aporta valor. Hasta que surja la necesidad, es preferible mantener una implementación directa. SRP se viola cuando la sobreingeniería oculta la responsabilidad principal.
Al intentar cumplir ISP, algunas personas crean una interfaz por cada método, generando fragmentos difíciles de gestionar.
interface PuedeGuardar {
void guardar();
}
interface PuedeActualizar {
void actualizar();
}
interface PuedeBorrar {
void borrar();
}
Este enfoque extremo produce “fragmentación de interfaces”. La recomendación es agrupar métodos relacionados por el mismo caso de uso. ISP se refiere a no imponer métodos innecesarios, no a atomizar sin criterio.
Forzar jerarquías para “aprovechar código” suele terminar en subclases que rompen el contrato implícito de la clase base.
class Archivo {
void abrir() { /* ... */ }
void cerrar() { /* ... */ }
}
class ArchivoSoloLectura extends Archivo {
@Override
void cerrar() {
throw new UnsupportedOperationException("No se puede cerrar");
}
}
La subclase invalida el comportamiento esperado: un archivo siempre debería poder cerrarse. La solución es usar composición o interfaces específicas.
Otra trampa consiste en declarar interfaces para cumplir DIP, pero seguir creando las implementaciones concretas dentro de la clase.
class ReporteService {
private final EmailSender emailSender;
ReporteService() {
this.emailSender = new EmailSender(); // dependencia rígida
}
}
Si la clase instancia la dependencia, la inversión no se produce. Es necesario inyectar la dependencia desde el exterior, preferentemente por constructor, para permitir pruebas y cambios tecnológicos.
Aplicar SOLID sin respaldarlo con pruebas automáticas puede introducir fallas silenciosas. Un clásico: refactorizar para cumplir SRP y romper flujos que nadie prueba.
Sin un entendimiento claro del negocio, se crean abstracciones equivocadas. Esto deriva en nombres genéricos o capas que duplican conceptos reales.
interface Manager {
void execute(Object request);
}
Este código dice poco sobre lo que hace el sistema. El principio SRP depende de modelar responsabilidades alineadas al dominio. La solución es trabajar con el equipo funcional y renombrar con intención.
Aplicar SOLID sin contexto lleva a decisiones que complican el proyecto. Algunas señales:
Recordá que SOLID es una guía, no un objetivo final. El diseño evoluciona con el proyecto.
Después de refactorizar, si el equipo no deja registro del motivo, el diseño se degrada rápidamente. Nuevas personas pueden revertir decisiones acertadas sin saberlo.
El principio KISS (Keep It Simple, Stupid) debe convivir con SOLID. Antes de introducir una nueva capa, preguntate si simplifica o complica la solución.
Aprender de estos errores acelera el dominio de SOLID. En la siguiente sección analizaremos cómo comparar código sin SOLID contra versiones refactorizadas para visualizar la mejora.