← Volver al Blog

Principios SOLID: Fundamentos para un Código Limpio y Mantenible

2 de julio de 2024

Los Principios SOLID son un conjunto de cinco reglas de diseño de software que promueven la escritura de código limpio, flexible y fácil de mantener. Estos principios fueron propuestos por el ingeniero de software Robert C. Martin (también conocido como "Uncle Bob") y se han convertido en una guía invaluable para los desarrolladores que buscan mejorar la calidad de su código y el diseño de sus aplicaciones.

Principio de Responsabilidad Única (Single Responsibility Principle - SRP)

El SRP establece que una clase debe tener una única responsabilidad y que esta responsabilidad debe estar encapsulada dentro de esa clase. En otras palabras, una clase debe tener una sola razón para cambiar. Esto mejora la modularidad y evita que una clase se vuelva demasiado compleja y difícil de mantener.

// Mal: Una clase con múltiples responsabilidades
class Usuario {
	obtenerDatosDeUsuario() {
		// Lógica para obtener los datos de usuario desde el servidor
	}

	guardarDatosDeUsuario(datos) {
		// Lógica para guardar los datos de usuario en el servidor
	}

	generarInformeDeUsuario() {
		// Lógica para generar un informe del usuario
	}
}

// Bien: Clases con una única responsabilidad
class Usuario {
	obtenerDatosDeUsuario() {
		// Lógica para obtener los datos de usuario desde el servidor
	}

	guardarDatosDeUsuario(datos) {
		// Lógica para guardar los datos de usuario en el servidor
	}
}

class GeneradorInformeUsuario {
	generarInformeDeUsuario() {
		// Lógica para generar un informe del usuario
	}
}

Principio de Abierto/Cerrado (Open/Closed Principle - OCP)

El OCP establece que las entidades del software (clases, módulos, etc.) deben estar abiertas para su extensión pero cerradas para su modificación. En otras palabras, cuando se agregan nuevas funcionalidades o requisitos, se deben agregar nuevas clases o extensiones en lugar de modificar las clases existentes.

// Mal: Modificar la clase existente para agregar una nueva funcionalidad
class Impresora {
	imprimirDocumento(documento) {
		// Lógica para imprimir el documento
	}

	// Nueva funcionalidad requerida: imprimirDocumentoEnColor
	imprimirDocumentoEnColor(documento) {
		// Lógica para imprimir el documento en color
	}
}

// Bien: Extender la clase para agregar la nueva funcionalidad
class Impresora {
	imprimirDocumento(documento) {
		// Lógica para imprimir el documento
	}
}

class ImpresoraEnColor extends Impresora {
	imprimirDocumentoEnColor(documento) {
		// Lógica para imprimir el documento en color
	}
}

Principio de Sustitución de Liskov (Liskov Substitution Principle - LSP)

El LSP establece que los objetos de una clase derivada deben poder sustituir a los objetos de la clase base sin afectar el comportamiento correcto del programa. En otras palabras, las clases derivadas deben ser totalmente sustituibles por sus clases base sin romper la funcionalidad.

// Mal: Violación del LSP
class Ave {
	volar() {
		// Lógica para volar
	}
}

class Pinguino extends Ave {
	volar() {
		// Los pingüinos no pueden volar, entonces esto viola el LSP
	}
}

// Bien: Respeto del LSP
class Ave {
	volar() {
		// Lógica para volar
	}
}

class Pinguino extends Ave {
	// Sin implementar el método volar, ya que los pingüinos no pueden volar
}

Principio de Segregación de Interfaces (Interface Segregation Principle - ISP)

El ISP establece que una clase no debe verse obligada a implementar interfaces que no utiliza. En lugar de tener una única interfaz grande, es mejor tener varias interfaces más pequeñas y específicas para cada tipo de cliente.

// Mal: Una única interfaz grande
interface Animal {
  caminar();
  nadar();
  volar();
}

class Pinguino implements Animal {
  caminar() {
    // Lógica para caminar
  }

  nadar() {
    // Lógica para nadar
  }

  // Los pingüinos no pueden volar, entonces esto es innecesario
  volar() {}
}

// Bien: Interfaces segregadas
interface Caminante {
  caminar();
}

interface Nadador {
  nadar();
}

interface Volador {
  volar();
}

class Pinguino implements Caminante, Nadador {
  caminar() {
    // Lógica para caminar
  }

  nadar() {
    // Lógica para nadar
  }
}

Principio de Inversión de Dependencias (Dependency Inversion Principle - DIP)

El DIP establece que los módulos de alto nivel no deben depender de módulos de bajo nivel. Ambos deben depender de abstracciones. Además, las abstracciones no deben depender de los detalles; los detalles deben depender de las abstracciones.

// Mal: Dependencia directa entre módulos de alto y bajo nivel
class EmailSender {
  enviarEmail() {
    // Lógica para enviar un email
  }
}

class UsuarioService {
  private emailSender: EmailSender;

  constructor() {
    this.emailSender = new EmailSender();
  }

  registrarUsuario() {
    // Lógica para registrar un usuario y enviar un email de bienvenida
    this.emailSender.enviarEmail();
  }
}

// Bien: Dependencia inversa a través de abstracciones
interface Mensajero {
  enviarMensaje();
}

class EmailSender implements Mensajero {
  enviarMensaje() {
    // Lógica para enviar un email
  }
}

class UsuarioService {
  private mensajero: Mensajero;

  constructor(mensajero: Mensajero) {
    this.mensajero = mensajero;
  }

  registrarUsuario() {
    // Lógica para registrar un usuario y enviar un mensaje de bienvenida
    this.mensajero.enviarMensaje();
  }
}

Estos Principios SOLID proporcionan una base sólida para desarrollar código limpio, coherente y mantenible. Al aplicar estos principios en tu desarrollo de software, podrás crear aplicaciones más robustas y flexibles, lo que facilitará la adición de nuevas funcionalidades y la escalabilidad a medida que tu proyecto crece.

Recuerda que la práctica y la comprensión profunda de estos principios te ayudarán a convertirte en un desarrollador más efectivo y respetuoso con la calidad del código que produces.

Gracias por leer y recuerda tomar agüita 💙