Introducción a SOLID
Introducción a SOLID
Antes de meternos con cada letra del acrónimo, necesitamos ponernos de acuerdo en algo fundamental: ¿por qué existen estos principios? No nacieron de un capricho académico ni de la moda de turno. Nacieron de un dolor muy concreto que cualquiera que haya mantenido un sistema durante más de seis meses conoce de memoria: el software funciona, sí, pero cada cambio se vuelve más caro, más lento y más peligroso que el anterior.
Este capítulo te da el vocabulario que vas a usar en todo el curso. Si entendés bien cohesión, acoplamiento y los cuatro síntomas del mal diseño, los cinco principios dejarán de parecer reglas sueltas y se revelarán como lo que realmente son: cinco estrategias distintas para atacar el mismo enemigo.
De dónde viene SOLID
A finales de los años 90 y comienzos de los 2000, Robert C. Martin —conocido en la comunidad como Uncle Bob— venía escribiendo y dando charlas sobre principios de diseño orientado a objetos. En el año 2000 publicó un ensayo influyente, Design Principles and Design Patterns, donde reunía una serie de principios pensados para gestionar las dependencias entre módulos de software.
Esos principios existían de forma dispersa. Lo que ocurrió después es que Michael Feathers, otro referente de la comunidad, notó que reordenando las iniciales de cinco de esos principios se formaba una palabra memorable: SOLID. El acrónimo pegó, y desde entonces se convirtió en una de las piezas de vocabulario más citadas (y más malinterpretadas) del diseño de software.
Conviene aclarar un matiz histórico: no todos los principios son invención de Uncle Bob. El Principio Abierto/Cerrado, por ejemplo, lo formuló originalmente Bertrand Meyer en 1988, y la regla de sustitución que da nombre al principio de Liskov viene de un trabajo de Barbara Liskov de 1987. Lo que hizo Uncle Bob fue seleccionarlos, reinterpretarlos con un enfoque práctico y agruparlos en un cuerpo coherente.
flowchart LR
A["Bertrand Meyer<br/>1988 · Open/Closed"] --> D["Robert C. Martin<br/>2000 · reúne los principios"]
B["Barbara Liskov<br/>1987 · sustitución"] --> D
C["Prácticas de diseño OO<br/>de la comunidad"] --> D
D --> E["Michael Feathers<br/>acuña el acrónimo SOLID"]
El problema que SOLID resuelve
El software útil cambia. Si un sistema deja de cambiar es porque nadie lo usa. Los requisitos se corrigen, aparecen funcionalidades nuevas, cambian las reglas del negocio, se integra un proveedor distinto. El cambio no es una anomalía: es el estado natural de cualquier proyecto vivo.
El problema es que la mayoría del código no está diseñado para cambiar. Está diseñado para funcionar hoy, con los requisitos de hoy, y punto. Y ahí es donde SOLID entra en escena. Su objetivo no es hacer el código más “elegante” ni más “profesional” en abstracto. Su objetivo es gestionar las dependencias para que, cuando llegue el cambio inevitable, el impacto quede contenido en lugar de propagarse como un incendio.
En una frase: SOLID es un conjunto de heurísticas para que el software resista el cambio.
Los cuatro síntomas del mal diseño
Uncle Bob describe cuatro “olores” que delatan un diseño podrido. No son bugs —el sistema puede funcionar perfectamente— sino signos de que la estructura interna se está degradando. Aprender a reconocerlos es el primer paso para curarlos.
1. Rigidez — es difícil de cambiar
Un diseño rígido es aquel en el que un cambio pequeño obliga a una cascada de cambios en módulos que aparentemente no tenían nada que ver. Querés modificar el formato de un número de teléfono y terminás tocando siete archivos, recompilando medio sistema y rezando para que nada se rompa.
El síntoma cotidiano: cuando el equipo estima que “un cambio de una línea” tarda dos días, casi siempre hay rigidez detrás.
2. Fragilidad — se rompe en lugares inesperados
La fragilidad es la tendencia del software a romperse en muchos lugares cada vez que se le hace un solo cambio, y lo peor: en lugares que no tienen relación conceptual con lo que tocaste. Arreglás el módulo de facturación y de repente falla la exportación de reportes.
El síntoma cotidiano: nadie quiere tocar cierto archivo “porque siempre que alguien lo toca, algo se rompe en otro lado”.
3. Inmovilidad — es difícil de reutilizar
Un diseño inmóvil contiene partes que podrían ser útiles en otro proyecto o en otro módulo, pero que están tan enredadas con el resto que separarlas cuesta más que reescribirlas desde cero. La lógica valiosa está ahí, pero atada con cadenas a dependencias que no querés arrastrar.
El síntoma cotidiano: “esta función de cálculo de impuestos sería perfecta para el otro sistema, pero se lleva media base de datos consigo”.
4. Viscosidad — es más fácil hacer un hack que hacerlo bien
La viscosidad tiene dos caras. La viscosidad del diseño ocurre cuando, frente a un cambio, hacer las cosas “de la manera correcta” (respetando la arquitectura) es más difícil y lento que hacer un parche sucio. Cuando eso pasa, los desarrolladores —aún los mejores— terminan eligiendo el hack, y el diseño se degrada un poco más con cada decisión.
El síntoma cotidiano: “sé que debería crear una clase nueva, pero es más rápido agregar otro if acá”.
flowchart TD
S["Mal diseño"] --> R["Rigidez<br/>un cambio arrastra muchos otros"]
S --> F["Fragilidad<br/>se rompe en lugares inesperados"]
S --> I["Inmovilidad<br/>no se puede reutilizar"]
S --> V["Viscosidad<br/>el hack es más fácil que hacerlo bien"]
Los cinco principios SOLID son, en el fondo, cinco tácticas para prevenir estos cuatro síntomas. Cuando en los próximos capítulos te preguntes “¿para qué sirve esto?”, la respuesta casi siempre será: para evitar la rigidez, la fragilidad, la inmovilidad o la viscosidad.
Los dos conceptos que sostienen todo: cohesión y acoplamiento
Si SOLID es el edificio, la cohesión y el acoplamiento son los cimientos. Estos dos conceptos son anteriores a SOLID (vienen de la ingeniería de software estructurada de los años 70, de la mano de Larry Constantine) y atraviesan absolutamente todos los principios. Vamos a definirlos con cuidado porque los usaremos sin parar.
Cohesión: cuán relacionadas están las cosas dentro de un módulo
La cohesión mide qué tan enfocado está un módulo: cuánto tienen que ver entre sí las responsabilidades que agrupa. Alta cohesión es buena: significa que todo lo que hay dentro de una clase pertenece junto, colabora hacia un mismo propósito, y por eso la clase es fácil de entender, nombrar y cambiar.
Una clase con baja cohesión es una bolsa de gatos: métodos que no tienen nada que ver conviviendo por accidente o por comodidad.
// ❌ Baja cohesión: esta clase hace de todo un poco
class Utilidades {
formatearFecha(fecha: Date): string {
return fecha.toISOString().split("T")[0];
}
calcularImpuesto(monto: number): number {
return monto * 0.18;
}
enviarEmail(destinatario: string, cuerpo: string): void {
// ... conecta con el servidor SMTP
}
validarPassword(password: string): boolean {
return password.length >= 8;
}
}
¿Qué tienen en común formatear una fecha, calcular un impuesto, enviar un email y validar una contraseña? Nada. Están juntas por pereza. Esta clase va a cambiar por mil razones distintas y nadie sabrá nunca qué esperar de ella.
// ✅ Alta cohesión: cada clase agrupa lo que pertenece junto
class CalculadoraImpuesto {
private readonly TASA_IGV = 0.18;
calcular(monto: number): number {
return monto * this.TASA_IGV;
}
calcularConBase(monto: number): { base: number; impuesto: number; total: number } {
const impuesto = this.calcular(monto);
return { base: monto, impuesto, total: monto + impuesto };
}
}
Ahora la clase tiene un solo tema. Todo lo que contiene gira en torno al cálculo del impuesto. Si algo cambia sobre impuestos, sabés exactamente dónde tocar; y si cambia cualquier otra cosa, esta clase ni se entera.
Acoplamiento: cuánto depende un módulo de otro
El acoplamiento mide cuánto conoce y depende un módulo de los detalles internos de otro. Bajo acoplamiento es bueno: cada pieza puede cambiar sin obligar a las demás a cambiar con ella. Alto acoplamiento es malo: las piezas están soldadas, y mover una arrastra a las otras (ahí tenés otra vez la rigidez y la fragilidad).
// ❌ Alto acoplamiento: NotificadorPedido crea y conoce los detalles concretos
class ServicioEmailGmail {
enviar(destinatario: string, asunto: string, cuerpo: string): void {
// ... lógica específica de Gmail
}
}
class NotificadorPedido {
private email = new ServicioEmailGmail(); // soldado a una implementación concreta
confirmar(pedidoId: string, cliente: string): void {
this.email.enviar(cliente, "Pedido confirmado", `Tu pedido ${pedidoId} está en camino`);
}
}
NotificadorPedido crea directamente un ServicioEmailGmail. Si mañana querés enviar por SMS, por WhatsApp o por otro proveedor de email, tenés que abrir y modificar NotificadorPedido. Está soldado a Gmail para siempre. Además, es imposible testear NotificadorPedido sin mandar emails reales.
// ✅ Bajo acoplamiento: NotificadorPedido depende de una abstracción
interface CanalNotificacion {
enviar(destinatario: string, mensaje: string): void;
}
class NotificadorPedido {
constructor(private readonly canal: CanalNotificacion) {}
confirmar(pedidoId: string, cliente: string): void {
this.canal.enviar(cliente, `Tu pedido ${pedidoId} está en camino`);
}
}
// La implementación concreta se decide desde afuera
class CanalEmail implements CanalNotificacion {
enviar(destinatario: string, mensaje: string): void {
// ... enviar por email
}
}
Ahora NotificadorPedido no sabe ni le importa cómo se envía la notificación. Depende de la abstracción CanalNotificacion, no de un detalle concreto. Podés pasarle un canal de email, de SMS, o un canal falso para tus tests, sin tocar una sola línea de NotificadorPedido. Esto es exactamente lo que persigue el Principio de Inversión de Dependencias que verás en el capítulo 6.
La regla de oro
Toda la ingeniería de software de calidad tira hacia el mismo lado:
flowchart LR
A["Alta cohesión<br/>(dentro de cada módulo)"] --> C["Software<br/>mantenible"]
B["Bajo acoplamiento<br/>(entre módulos)"] --> C
Alta cohesión hacia adentro, bajo acoplamiento hacia afuera. Si tuvieras que resumir el diseño orientado a objetos en una sola frase, sería esa. SOLID no es más que cinco maneras concretas de conseguirla.
El acrónimo: los cinco principios de un vistazo
| Letra | Principio | En una frase |
|---|---|---|
| S | Single Responsibility Principle (SRP) | Una clase debe tener una sola razón para cambiar. |
| O | Open/Closed Principle (OCP) | El software debe estar abierto a la extensión pero cerrado a la modificación. |
| L | Liskov Substitution Principle (LSP) | Los subtipos deben ser sustituibles por sus tipos base sin romper el programa. |
| I | Interface Segregation Principle (ISP) | Ningún cliente debería depender de métodos que no usa. |
| D | Dependency Inversion Principle (DIP) | Depender de abstracciones, no de implementaciones concretas. |
Fijate en algo importante: los cinco principios se refuerzan entre sí. Separar responsabilidades (SRP) hace más fácil segregar interfaces (ISP); segregar interfaces hace más fácil invertir dependencias (DIP); invertir dependencias es lo que te permite extender sin modificar (OCP); y todo eso solo funciona si tus subtipos respetan sus contratos (LSP). No son cinco reglas aisladas: son cinco vistas del mismo objetivo. En el capítulo 7 veremos cómo encajan trabajando juntos sobre un caso real.
SOLID es una heurística, no un dogma
Terminemos con una advertencia que vale oro y que repetiremos a lo largo del curso: SOLID no es un conjunto de mandamientos. Es un conjunto de heurísticas —reglas de buen juicio que funcionan la mayoría de las veces, no leyes universales.
Aplicar SOLID a rajatabla, en cada clase, desde la primera línea de código, es una receta para la sobre-ingeniería: capas de abstracción que nadie necesita, interfaces con una sola implementación que jamás tendrá otra, indirecciones que solo sirven para que leer el código sea más difícil. Ese es un problema tan real como el diseño rígido, y le dedicamos el capítulo 9 entero.
La pregunta correcta nunca es “¿estoy cumpliendo SOLID?”. La pregunta correcta es: “¿este diseño me va a doler cuando llegue el cambio que puedo anticipar?”. SOLID es una caja de herramientas para responder esa pregunta, no una checklist para tildar casillas.
Resumen
- SOLID son cinco principios de diseño orientado a objetos reunidos por Robert C. Martin (Uncle Bob) hacia el año 2000; el acrónimo lo acuñó Michael Feathers. Algunos principios son anteriores (Meyer, Liskov).
- Su objetivo es gestionar dependencias para que el software resista el cambio, que es inevitable en todo proyecto vivo.
- El mal diseño se delata con cuatro síntomas: rigidez (difícil de cambiar), fragilidad (se rompe en lugares inesperados), inmovilidad (no se puede reutilizar) y viscosidad (el hack es más fácil que hacerlo bien).
- Dos conceptos base atraviesan todo SOLID: cohesión (buscá que sea alta dentro de cada módulo) y acoplamiento (buscá que sea bajo entre módulos).
- La regla de oro: alta cohesión hacia adentro, bajo acoplamiento hacia afuera.
- SOLID es una heurística, no un dogma. Aplicarlo sin criterio lleva a la sobre-ingeniería, un problema tan grave como el diseño rígido.
Con este vocabulario en la mano, ya podemos atacar el primer principio, el que más se malinterpreta de los cinco: