IntermedioBuenas prácticas

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.

sin invertir Dominio Postgres invertido ✓ Dominiodefine «Repositorio» Postgres

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”.