Principios SOLID: Qué son y cómo aplicarlos

12 de diciembre de 2024

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