¿Que son los Principios SOLID? Son cinco principios de diseno orientado a objetos que, cuando se aplican juntos, hacen que el codigo sea mas mantenible, flexible y facil de entender. El acronimo SOLID fue acunado por Robert C. Martin (Uncle Bob).
Imagina que estas construyendo un edificio. Si los cimientos no son solidos, todo se vendra abajo eventualmente. Lo mismo ocurre con el software.
Los principios SOLID son como las reglas de la buena construccion, pero para tu codigo.
Principio de Responsabilidad Unica
"Una clase debe tener una, y solo una, razon para cambiar."
Un cocinero en un restaurante se especializa en preparar platos, no en tomar ordenes, lavar platos y cobrar a los clientes.
class Usuario {
// Datos del usuario
private String nombre;
private String email;
// Guardar usuario (responsabilidad de persistencia)
public void guardarEnBaseDeDatos() { ... }
// Enviar email (responsabilidad de comunicacion)
public void enviarEmailBienvenida() { ... }
// Validar datos (responsabilidad de validacion)
public boolean validarEmail() { ... }
}
class Usuario {
private String nombre;
private String email;
// Solo getters y setters
}
class UsuarioRepository {
public void guardar(Usuario usuario) { ... }
}
class EmailService {
public void enviarBienvenida(Usuario usuario) { ... }
}
class ValidadorUsuario {
public boolean validarEmail(String email) { ... }
}
Principio Abierto/Cerrado
"Las entidades de software deben estar abiertas para su extension, pero cerradas para su modificacion."
Un control remoto universal que puede programarse para nuevos dispositivos sin tener que cambiar su diseno interno.
class CalculadoraImpuestos {
public double calcularImpuesto(Producto producto) {
if (producto.tipo == "ALIMENTO") {
return producto.precio * 0.07;
} else if (producto.tipo == "ELECTRONICO") {
return producto.precio * 0.18;
}
// Si anadimos un nuevo tipo, tenemos que modificar esta clase
return 0;
}
}
interface CalculadorImpuesto {
double calcularImpuesto(double precio);
}
class ImpuestoAlimentos implements CalculadorImpuesto {
public double calcularImpuesto(double precio) {
return precio * 0.07;
}
}
class ImpuestoElectronicos implements CalculadorImpuesto {
public double calcularImpuesto(double precio) {
return precio * 0.18;
}
}
// Para anadir un nuevo tipo, solo creamos una nueva clase
class ImpuestoRopa implements CalculadorImpuesto {
public double calcularImpuesto(double precio) {
return precio * 0.12;
}
}
Principio de Sustitucion de Liskov
"Las clases derivadas deben poder sustituir a sus clases base sin alterar el comportamiento esperado."
Si reemplazas a un chef con su aprendiz, los clientes deben seguir recibiendo comida de la misma calidad.
class Ave {
public void volar() {
System.out.println("Volando...");
}
}
class Pinguino extends Ave {
@Override
public void volar() {
// Los pinguinos no pueden volar, esto rompe el principio
throw new UnsupportedOperationException("Los pinguinos no vuelan");
}
}
interface Ave {
void moverse();
}
class AveVoladora implements Ave {
public void moverse() {
System.out.println("Volando...");
}
}
class Pinguino implements Ave {
public void moverse() {
System.out.println("Nadando y caminando...");
}
}
Principio de Segregacion de Interfaces
"Los clientes no deben verse forzados a depender de interfaces que no utilizan."
Un smartphone moderno tiene muchas funciones, pero un usuario mayor quizas solo quiera hacer llamadas y enviar mensajes, no necesita todas las opciones avanzadas.
interface Trabajador {
void trabajar();
void comer();
void dormir();
}
// Un robot puede trabajar pero no necesita comer ni dormir
class Robot implements Trabajador {
public void trabajar() {
// Implementacion
}
public void comer() {
// Los robots no comen, metodo vacio o excepcion
}
public void dormir() {
// Los robots no duermen, metodo vacio o excepcion
}
}
interface Trabajable {
void trabajar();
}
interface Comedor {
void comer();
}
interface Durmiente {
void dormir();
}
class Humano implements Trabajable, Comedor, Durmiente {
// Implementa todos los metodos
}
class Robot implements Trabajable {
// Solo implementa lo que necesita
public void trabajar() {
// Implementacion
}
}
Principio de Inversion de Dependencias
"Los modulos de alto nivel no deben depender de modulos de bajo nivel. Ambos deben depender de abstracciones."
Cuando enchufas un aparato, no te preocupas por como se genera la electricidad (carbon, nuclear, solar). El enchufe es una abstraccion que permite que todo funcione sin conocer los detalles.
class NotificadorEmail {
public void enviar(String mensaje, String destinatario) {
// Logica para enviar email
}
}
class ServicioNotificacion {
private NotificadorEmail notificador = new NotificadorEmail();
public void notificarUsuario(Usuario usuario, String mensaje) {
// Dependencia directa a una implementacion concreta
notificador.enviar(mensaje, usuario.getEmail());
}
}
interface Notificador {
void enviar(String mensaje, String destinatario);
}
class NotificadorEmail implements Notificador {
public void enviar(String mensaje, String destinatario) {
// Logica para enviar email
}
}
class NotificadorSMS implements Notificador {
public void enviar(String mensaje, String destinatario) {
// Logica para enviar SMS
}
}
class ServicioNotificacion {
private final Notificador notificador;
// Inyeccion de dependencia
public ServicioNotificacion(Notificador notificador) {
this.notificador = notificador;
}
public void notificarUsuario(Usuario usuario, String mensaje) {
notificador.enviar(mensaje, usuario.getContacto());
}
}
Codigo mas facil de mantener y actualizar con el tiempo.
Facilita el crecimiento de la aplicacion sin reescribir grandes partes.
Codigo mas predecible y con menos efectos secundarios inesperados.
Facilita la escritura de pruebas unitarias efectivas.
Los principios SOLID no son reglas rigidas, sino guias para escribir mejor codigo. Recuerda: