Principios SOLID explicados con ejemplos claros
Principios SOLID: Qué son, ejemplos y cómo los he aplicado en mis proyectos
Los principios SOLID son una guía fundamental para escribir software más limpio, escalable y fácil de mantener. Aunque el término existe desde principios del 2000, todavía hay muchos equipos que no los aplican de forma consciente, y eso provoca diseños rígidos, difíciles de probar o de reutilizar.
En SernaLabs los he ido incorporando en mis proyectos recientes, y aquí te cuento —de forma práctica y aplicada— qué significa cada principio y cómo me han ayudado a mejorar la calidad de mi código.
¿Qué significan los principios SOLID?
- Single Responsibility (Responsabilidad Única)
- Open/Closed (Abierto/Cerrado)
- Liskov Substitution (Sustitución de Liskov)
- Interface Segregation (Segregación de Interfaces)
- Dependency Inversion (Inversión de Dependencias)
A continuación, te explico cada uno con ejemplos claros y aplicados.
Single Responsibility Principle (SRP)
Este principio indica que una clase, módulo o microservicio debe tener una sola razón para cambiar. Una sola responsabilidad.
Ejemplo sencillo:
Si tienes una clase Libro, debería atender únicamente lo relacionado con préstamos o administración del libro: Prestar, Devolver, Retirar. Nada de agregar métodos como VerHistorialUsuarios o EnviarAvisos, porque eso le pertenece a otras responsabilidades.
Cuando respetas este principio:
- El código es más fácil de probar.
- Las clases se vuelven más predecibles.
- Los cambios no rompen funcionalidades no relacionadas.
public class Libro {
public string Titulo { get; set; }
public bool EnPrestamo { get; private set; }
public void Prestar() {
if (!EnPrestamo) EnPrestamo = true;
}
public void Devolver() {
if (EnPrestamo) EnPrestamo = false;
}
}
// Otra responsabilidad → otra clase
public class NotificadorUsuario {
public void EnviarAviso(string mensaje) {
// Lógica para enviar correos o notificaciones
}
}
Open-Closed Principle (OCP)
El código debe estar cerrado a modificaciones pero abierto a extensiones. Es decir, no alteras código que ya funciona; lo amplías creando nuevas clases o heredando comportamientos.
Este ejemplo lo utilicé en un proyecto de matemáticas computacionales, calculando distancias y productos vectoriales:
public abstract class Punto {
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
}
public abstract class MathVector {
public Punto A { get; set; }
public Punto B { get; set; }
public float ProductoPunto => (A.X * B.X) + (A.Y * B.Y) + (A.Z * B.Z);
public float ProductoCruz => (A.Y * B.Z) + (A.Z * B.X) + (A.X * B.Y)
- (A.Y * B.X) - (A.Z * B.Y) - (A.X * B.Z);
}
public class Punto3D : Punto {
public Punto3D(float x, float y, float z) {
X = x;
Y = y;
Z = z;
}
}
public class Vector3D : MathVector {
public Vector3D(Punto origen, Punto destino) {
A = origen;
B = destino;
}
public double ObtenerDistancia() {
float X = A.X - B.X;
float Y = A.Y - B.Y;
float Z = A.Z - B.Z;
return Math.Sqrt((X * X) + (Y * Y) + (Z * Z));
}
}
Con este diseño, puedo extender comportamientos sin modificar las clases base.
Liskov Substitution Principle (LSP)
Una clase hija debe poder sustituir a su clase base sin alterar el correcto funcionamiento del sistema.
En otras palabras:
Si heredas, compórtate como tu clase padre.
En proyectos reales, LSP te obliga a diseñar herencias coherentes y evita “hijitos rebeldes” que rompen lógica al reemplazarlos por la clase base.
public abstract class Figura {
public abstract double Area();
}
public class Cuadrado : Figura {
public double Lado { get; set; }
public override double Area() => Lado * Lado;
}
public class Circulo : Figura {
public double Radio { get; set; }
public override double Area() => Math.PI * Radio * Radio;
}
public class CalculadoraArea {
public double Calcular(Figura figura) {
return figura.Area(); // No importa si es círculo o cuadrado, funciona igual
}
}
Interface Segregation Principle (ISP)
Este principio promueve interfaces pequeñas y específicas en vez de interfaces enormes y genéricas.
¿Por qué es tan importante?
- Evitas obligar a una clase a implementar métodos que no necesita.
- Fomentas diseños más limpios y modulares.
- Ayuda a que cada interfaz represente una responsabilidad concreta.
En la práctica, es complementario a SRP.
public interface IPrestable {
void Prestar();
void Devolver();
}
public interface ICatalogable {
string ObtenerDescripcion();
}
public class Revista : IPrestable, ICatalogable {
public string Nombre { get; set; }
public void Prestar() { /* ... */ }
public void Devolver() { /* ... */ }
public string ObtenerDescripcion() {
return $"Revista: {Nombre}";
}
}
public class PosterDecorativo : ICatalogable {
public string Titulo { get; set; }
public string ObtenerDescripcion() {
return $"Póster: {Titulo}";
}
}
Dependency Inversion Principle (DIP)
Este principio ayuda a desacoplar el código. En lugar de depender de implementaciones concretas, dependes de abstracciones como interfaces.
Ejemplo típico:
- No instancias directamente
new SqlRepository(). - Recibes
IRepositoryen el constructor.
¿El resultado?
- Clases más fáciles de probar.
- Mayor flexibilidad para sustituir implementaciones.
- Menos dependencia rígida entre módulos.
public interface IRepositorioUsuarios {
void Guardar(string nombre);
}
public class SqlRepositorioUsuarios : IRepositorioUsuarios {
public void Guardar(string nombre) {
// Guardar en SQL
}
}
public class ServicioRegistro {
private readonly IRepositorioUsuarios _repositorio;
public ServicioRegistro(IRepositorioUsuarios repositorio) {
_repositorio = repositorio;
}
public void Registrar(string nombre) {
_repositorio.Guardar(nombre);
}
}
Conclusión
Adoptar los principios SOLID puede parecer complicado al inicio, pero una vez que los aplicas, tus proyectos se vuelven más flexibles, ordenados y fáciles de mantener. Incluso si al principio implica escribir más código, la ganancia está en la claridad, la escalabilidad y la capacidad de prueba.
Mi recomendación: empieza con uno o dos principios, aplícalos en tus proyectos personales, y poco a poco verás cómo tu forma de diseñar software evoluciona.
Si quieres conocer un poco más sobre mí y mi trabajo con tecnología e IA, puedes visitar mi página de Quién soy y qué hago. También encontrarás detalles sobre los servicios que ofrezco en Por qué elegirme / Qué ofrezco. Y si te interesa la integración de IA con salud y datos médicos, echa un vistazo a API Médica.
Comentarios
Publicar un comentario