Los principios SOLID son un conjunto de buenas prácticas en la programación orientada a objetos que ayudan a escribir código más mantenible, escalable y flexible. Fueron formulados por Robert C. Martin (Uncle Bob) y se consideran fundamentales para el diseño de software. Veamos cada uno de ellos con ejemplos en JavaScript.
1. Principio de Responsabilidad Única (Single Responsibility Principle - SRP)
Un módulo o clase debe tener una única razón para cambiar, es decir, debe realizar una sola tarea o responsabilidad.
Ejemplo incorrecto:
class ReportGenerator {
generatePDF(data) {
// Generar PDF
}
sendEmail(report) {
// Enviar por correo
}
}
Ejemplo correcto:
class PDFGenerator {
generate(data) {
// Generar PDF
}
}
class EmailSender {
send(report) {
// Enviar correo
}
}
Al separar la generación del PDF del envío por correo, cumplimos con SRP.
2. Principio de Abierto/Cerrado (Open/Closed Principle - OCP)
Las clases deben estar abiertas para extensión pero cerradas para modificación. Esto significa que debemos poder agregar nuevas funcionalidades sin alterar el código existente.
Ejemplo incorrecto:
class Descuento {
calcular(monto, tipo) {
if (tipo === "estudiante") {
return monto * 0.8;
} else if (tipo === "vip") {
return monto * 0.7;
}
return monto;
}
}
Ejemplo correcto (Usando polimorfismo):
class Descuento {
calcular(monto) {
return monto;
}
}
class DescuentoEstudiante extends Descuento {
calcular(monto) {
return monto * 0.8;
}
}
class DescuentoVIP extends Descuento {
calcular(monto) {
return monto * 0.7;
}
}
De esta forma, podemos agregar nuevos tipos de descuentos sin modificar la clase base.
3. Principio de Sustitución de Liskov (Liskov Substitution Principle - LSP)
Las subclases deben poder sustituir a sus clases base sin afectar el funcionamiento del programa.
Ejemplo incorrecto:
class Pato {
volar() {
console.log("Volando...");
}
}
class PatoDeGoma extends Pato {
volar() {
throw new Error("Los patos de goma no pueden volar");
}
}
Ejemplo correcto:
class Pato {
nadar() {
console.log("Nadando...");
}
}
class PatoVolador extends Pato {
volar() {
console.log("Volando...");
}
}
class PatoDeGoma extends Pato {
chirriar() {
console.log("Chirrido");
}
}
Ahora, todas las clases hijas cumplen con LSP porque respetan el comportamiento de la clase base sin romper funcionalidades.
4. Principio de Segregación de Interfaces (Interface Segregation Principle - ISP)
Una interfaz no debe obligar a sus implementaciones a depender de métodos que no usan.
Ejemplo incorrecto:
class Trabajador {
trabajar() {}
comer() {}
}
class Robot extends Trabajador {
comer() {
throw new Error("Los robots no comen");
}
}
Ejemplo correcto:
class Trabajador {
trabajar() {}
}
class SerHumano extends Trabajador {
comer() {}
}
Ahora, Robot
no está forzado a implementar comer()
, cumpliendo con ISP.
5. Principio de Inversión de Dependencias (Dependency Inversion Principle - DIP)
Los módulos de alto nivel no deben depender de módulos de bajo nivel, sino de abstracciones.
Ejemplo incorrecto:
class MySQLDatabase {
connect() {
console.log("Conectado a MySQL");
}
}
class Aplicacion {
constructor() {
this.db = new MySQLDatabase();
}
}
Ejemplo correcto (Usando abstracción):
class Database {
connect() {}
}
class MySQLDatabase extends Database {
connect() {
console.log("Conectado a MySQL");
}
}
class Aplicacion {
constructor(db) {
this.db = db;
}
}
const database = new MySQLDatabase();
const app = new Aplicacion(database);
De esta forma, podemos cambiar la base de datos sin modificar la Aplicacion
.
Aplicar los principios SOLID mejora la estructura de nuestro código, facilitando su mantenimiento y escalabilidad. Adoptar estas buenas prácticas permite desarrollar software más robusto y flexible en el tiempo. 🚀