Los principios SOLID
Cinco heurísticas sobre cómo repartir responsabilidades y dependencias para que el cambio no duela — con su letra pequeña.
En esta página
SOLID son cinco principios sobre la misma pregunta: ¿cómo organizo el código para que cambiarlo no sea peligroso? No son leyes — son heurísticas destiladas de décadas de mantenimiento doloroso. Entendidos como brújula funcionan; recitados como dogma producen arquitecturas de encaje de bolillos. Vamos con ambos.
S — Responsabilidad única (SRP)
Un módulo debería tener un solo motivo para cambiar.
El motivo de cambio lo definen las personas: si contabilidad puede pedirte cambios en una clase, y también diseño, y también sistemas, esa clase tiene tres responsabilidades:
// ❌ Tres motivos de cambio en una clase
class Informe {
calcularTotales() {} // cambia si cambian las reglas de negocio
formatearHTML() {} // cambia si cambia el diseño
guardarEnDisco() {} // cambia si cambia la infraestructura
}
// ✅ Un motivo por pieza: calculadora, presentador, repositorio
La señal de violación más fiable no es el tamaño — es la palabra “y” al describir qué hace: “calcula los totales y los formatea y los guarda”.
O — Abierto/cerrado (OCP)
Abierto a extensión, cerrado a modificación: añadir un caso nuevo no debería tocar código que ya funciona.
Es el principio que viste funcionando en Strategy: el switch de métodos de envío que crece por dentro viola OCP; el mapa de estrategias donde añades una entrada lo cumple. No significa “no modifiques nunca nada” — significa que los puntos de variación previsibles merecen una costura para extender sin operar a corazón abierto.
L — Sustitución de Liskov (LSP)
Donde el código espera el tipo base, cualquier subtipo debe funcionar sin sorpresas.
class Ave { }
class Aguila extends Ave { volar() { /* ... */ } }
class Pinguino extends Ave {
volar() { throw new Error('los pingüinos no vuelan'); } // ❌ bomba de relojería
}
Todo el que reciba un Ave y la haga volar explotará con un pingüino — la jerarquía miente. LSP dice que la herencia se define por el contrato observable, no por el parecido del mundo real: si un subtipo necesita lanzar donde el padre prometía funcionar, endurecer precondiciones o devolver menos, el modelo está mal cortado (aquí: Ave y AveVoladora separadas, o composición en lugar de herencia).
I — Segregación de interfaces (ISP)
Nadie debería verse obligado a depender de métodos que no usa.
Una interfaz Impresora con imprimir(), escanear() y enviarFax() obliga a la impresora básica a implementar (¿lanzando?) dos métodos que no tiene — que es exactamente el problema de Liskov, fabricado en la interfaz. Interfaces pequeñas y componibles (Imprime, Escanea) dejan que cada clase firme solo lo que cumple, y que cada consumidor pida solo lo que necesita.
D — Inversión de dependencias (DIP)
Las políticas de alto nivel no dependen de detalles: ambos dependen de abstracciones.
La flecha se da la vuelta: el detalle implementa la interfaz que el dominio posee. Quien define el contrato manda.
Es la regla de dependencia de la arquitectura en capas formulada como principio — y la razón práctica de que puedas testear tu dominio pasándole un repositorio falso. La “inyección de dependencias” es solo la mecánica de entregar la implementación; la inversión es la idea.
La letra pequeña
Si te quedas con una sola frase: el código sano tiene piezas con un propósito claro, y las flechas de dependencia apuntan de lo concreto hacia lo estable. Los cinco principios son variaciones de esa frase — igual que DRY y los nombres significativos son variaciones de “el código se escribe para quien lo lee”.