¿Alguna vez has intentado armar un mueble sin instrucciones? Probablemente termines con tornillos sobrantes, piezas mal colocadas y una frustración enorme. Lo mismo ocurre en el desarrollo de software: sin reglas claras, el código se vuelve un caos difícil de mantener. Aquí es donde entran los principios SOLID, cinco reglas que te ayudarán a escribir código limpio, flexible y profesional, incluso si estás empezando.
Hoy, te explicaré cada principio solid con ejemplos sencillos y analogías cotidianas. Al final, no solo entenderás qué son, sino por qué son esenciales para crear aplicaciones que no colapsen ante el primer cambio.
Te dejo el repositorio con los ejemplos aquí.
1. Principio SOLID -S- de Responsabilidad Única (SRP): «Un Soldado, Una Misión»
¿Qué dice?
«Una clase debe tener una sola responsabilidad, es decir, solo una razón para cambiar» .
Imagina un chef en un restaurante. Su trabajo es cocinar, ¿verdad? Pero si también tuviera que limpiar los platos, atender a los clientes y manejar las redes sociales, probablemente quemaría la comida. Lo mismo pasa en el código: si una clase hace demasiadas cosas, cualquier cambio puede romper todo.
Ejemplo Violado:
public class Usuario
{
public string Nombre { get; set; }
public string Email { get; set; }
public void EnviarCorreoBienvenida()
{
Console.WriteLine($"Enviando correo a {Email}: ¡Bienvenido, {Nombre}!");
}
}
Aquí, la clase Usuario
no solo almacena datos del usuario, sino que también envía correos. Si luego quieres cambiar cómo se envían los emails, tendrías que modificar esta clase, arriesgando errores en la lógica del usuario.
Solución:
public class Usuario
{
public string Nombre { get; set; }
public string Email { get; set; }
}
public class ServicioCorreo
{
public void EnviarCorreoBienvenida(Usuario usuario)
{
Console.WriteLine($"Enviando correo a {usuario.Email}: ¡Bienvenido, {usuario.Nombre}!");
}
}
Ahora, cada clase tiene una tarea única. Si cambia el servicio de correo, no afectarás la clase Usuario
.
2. Principio SOLID -O- Abierto/Cerrado (OCP): «Extiende, No Modifiques»
¿Qué dice?
«Las clases deben estar abiertas para extensión pero cerradas para modificación».
Piensa en tu teléfono: puedes instalar nuevas apps sin alterar su sistema operativo. Así debería ser tu código: poder agregar funcionalidades sin reescribir lo que ya funciona.
Ejemplo Violado:
public class ProcesadorPagos
{
public void Procesar(string metodo, float importe)
{
if (metodo == "PayPal")
{
// Lógica para PayPal
}
else if (metodo == "Tarjeta")
{
// Lógica para tarjeta
}
}
}
Si añades un nuevo método como «Bitcoin», tendrías que modificar esta clase, lo que podría introducir errores.
Solución:
public interface IMetodoPago
{
void Procesar(string metodo, float importe);
}
public class PayPal : IMetodoPago
{
public void Procesar(string metodo, float importe)
{
// Lógica específica para PayPal
}
}
public class Bitcoin : IMetodoPago
{
public void Procesar(string metodo, float importe)
{
// Lógica específica para Bitcoin
}
}
Ahora, para agregar un nuevo método, solo creas una clase que implemente la interfaz IMetodoPago
, sin tocar el código existente, cumpliendo así con los principios solid.
3. Principio -L- de Sustitución de Liskov (LSP): «Heredar sin Sorpresas»
¿Qué dice?
«Las clases hijas deben poder usarse en lugar de las clases padres sin alterar el comportamiento esperado».
Imagina que heredas un coche eléctrico de tu padre. Si al usarlo descubres que no enciende porque necesita gasolina, romperías la lógica esperada. Lo mismo pasa en la programación.
Ejemplo Violado:
public class Pajaro
{
public virtual void Volar()
{
Console.WriteLine("Volando...");
}
}
public class Pinguino : Pajaro
{
public override void Volar() {
throw new Exception("¡Los pingüinos no vuelan!");
}
}
Si sustituyes Pajaro
por Pinguino
, el código fallará inesperadamente.
Solución:
public interface IAnimalVolador
{
void Volar();
}
public interface IAnimalNadador
{
void Nadar();
}
public class Aguila : IAnimalVolador
{
public void Volar()
{
/* ... */
}
}
public class Pinguino : IAnimalNadador
{
public void Nadar()
{
/* ... */
}
}
Separando interfaces, evitamos forzar comportamientos no deseados.
4. Principio SOLID -I- de Segregación de Interfaces (ISP): «Menos es Más»
¿Qué dice este principio solid?
«Ninguna clase debe verse obligada a implementar métodos que no usa».
Es como un menú de restaurante: si eres vegetariano, no quieres ver opciones de carne. Las interfaces deben ser específicas para lo que cada clase necesita.
Ejemplo Violado:
public class ImpresoraBasica : IDispositivoOficina
{
public void Imprimir()
{
/* ... */
}
public void Escanear()
{
throw new Exception("No soportado");
}
public void Fax()
{
throw new Exception("No soportado");
}
}
La ImpresoraBasica
implementa métodos que no usa, lo que genera código innecesario.
Solución:
public interface IImprimible
{
void Imprimir();
}
public interface IEscaneable
{
void Escanear();
}
public class ImpresoraAvanzada : IImprimible, IEscaneable
{
public void Escanear()
{
/* ... */
}
public void Imprimir()
{
/* ... */
}
}
Ahora, cada clase implementa solo lo que necesita.
5. Principio -D- de Inversión de Dependencias (DIP): «Depende de Abstracciones, No de Concretos»
¿Qué dice?
«Los módulos de alto nivel no deben depender de los de bajo nivel. Ambos deben depender de abstracciones».
Piensa en un control remoto: no depende de pilas específicas (AA, AAA), sino de la abstracción de «una fuente de energía». Así, puedes cambiar las pilas sin modificar el control.
Ejemplo Violado:
public class ServicioUsuarios
{
public void GuardarUsuario()
{
var baseDatos = new MyDataBase(); // Dependencia directa
baseDatos.Guardar();
}
}
Si cambias a MongoDB, tendrías que modificar ServicioUsuarios
.
Solución:
public interface IBaseDatos
{
void Guardar();
}
public class MySQL : IBaseDatos
{
public void Guardar() { /* ... */ }
}
public class ServicioUsuarios
{
private readonly IBaseDatos _baseDatos;
public ServicioUsuarios(IBaseDatos baseDatos)
{
_baseDatos = baseDatos;
}
public void GuardarUsuario()
{
_baseDatos.Guardar();
}
}
Ahora, ServicioUsuarios
depende de una interfaz, permitiendo cambiar la base de datos fácilmente.
Conclusión
Los principios SOLID no son solo teoría: son herramientas prácticas que evitan que tu código se convierta en un «spaguetti» imposible de mantener. Al aplicarlos:
- Reduces bugs: Menos acoplamiento significa menos efectos secundarios al hacer cambios.
- Facilitas el trabajo en equipo: Cada clase tiene una responsabilidad clara, evitando conflictos.
- Ahorras tiempo: Extender funcionalidades es más rápido y seguro.
Si estás empezando, puede parecer abrumador, pero como dice el refrán: «La práctica hace al maestro». Empieza aplicando un principio a la vez, refactoriza proyectos pequeños y verás cómo tu código evoluciona de «funciona» a «es una obra de arte» 🔥.
¿Listo para convertirte en un buen Desarrollador ? ¡El primer paso es empezar!