DEV Community

Cover image for Principios SOLID
Sebastián Barrera Herrera
Sebastián Barrera Herrera

Posted on

Principios SOLID

Al iniciar en la programación, hay pasos que es necesario recorrer, y uno de ellos son los principios SOLID, unas guías que te ayudarán a mejorar la calidad del código, sin importar el nivel de experiencia que tengas. Para los que comenzamos este camino, puede ser abrumador tener presente estos conceptos y aplicarlos en nuestro código. Muchas veces, las primeras explicaciones que recibimos ni siquiera mencionan el código limpio, y tiene sentido al principio; lo importante son las bases y no asustar a los principiantes con conceptos mucho más complejos. El problema surge cuando, con el tiempo, las bases son sólidas pero el código empieza a volverse un pergamino de lo largo que llega a ser. Las malas prácticas y el poco conocimiento del tema se convierten en constantes, y a partir de ahí, las malas prácticas se vuelven la norma.

Para cambiar esta realidad, se necesita un poco más de esfuerzo. Ya aprendiste las bases y sabes cómo chutar el balón, ahora se requiere un entrenador que hable de estrategia y te enseñe a trabajar en equipo. Así sucede con los pergaminos, lejos de ser sagrados, se convierten en un dolor de cabeza y te empiezan a frenar el aprendizaje. Como mencioné anteriormente, se requiere una guía, un buen entrenador que te oriente y te enseñe el camino que él ya recorrió. En los tiempos de los cursos en línea, parece imposible tener un sensei que se convierta en tu bastión, pero, aunque sea a través de una grabación, hay muchos que tienen la vocación y uno siempre encuentra a alguien que realmente se esmera y lo hace muy bien.

Hablo sobre mi experiencia y lo que he visto en este proceso; todavía lo estoy recorriendo, mi código aún es un pergamino y se va a demorar en ser sólido. Para otra persona puede ser distinto, puede tener otra opinión o quizá no les tocó hacer estas millas extras, pero para aquellos que se sientan identificados, les voy a comentar y poner ejemplos de las recomendaciones que serán los principios del cambio en mi código.

S: Single Responsibility Principle (Principio de Responsabilidad Única)

Este primer principio habla de independencia y responsabilidades. Nos dice que, sin importar lo grande o pequeña que sea nuestra construcción, cada parte debe tener una responsabilidad única. No cargar a una clase, función u objeto con más peso del que puede levantar nos ayuda a evitar problemas futuros.

class Student {
    constructor(public name: string, public age: number) {}
}

class School {
    constructor(public nameSchool: string, public type: string) {}
}

class Certificate {
    constructor(private student: Student, private school: School) {}

    generateCertificate(): string {
        return `Certificate for ${this.student.name}, age ${this.student.age}, from ${this.school.nameSchool} (${this.school.type})`;
    }
}
Enter fullscreen mode Exit fullscreen mode

O: Open/Closed Principle (Principio de Abierto/Cerrado)

Este importante principio nos dice que toda clase debe estar abierta a extenderse, pero cerrada a modificarse. Puedo tener una clase base que me sirva de referencia en varios lugares; esta clase, al ser eficiente, puede extenderse y así el funcionamiento de sus clases hijas mejorará. Sin embargo, debo evitar modificar la clase principal para que el funcionamiento de la clase hija sea óptimo. Hijo de tigre sale pintado.

interface Car {
    inspect(): string;
}

class Mazda implements Car {
    constructor(public brand: string, public model: string) {}

    inspect(): string {
        return `Inspecting Mazda: ${this.brand} ${this.model}`;
    }
}

...More class
Enter fullscreen mode Exit fullscreen mode

L: Liskov Substitution Principle (Principio de Sustitución de Liskov)

Este principio lleva su nombre en honor a quien lo creó, Barbara Liskov. Personalmente, ha sido el que más me ha costado entender, pero haré el esfuerzo de ser lo más claro posible para que lo entiendas. Cada clase que hereda de otra puede usarse como su clase padre sin necesidad de conocer las diferencias entre ellas y debería actuar sin causar errores inesperados.

class Animal {
    makeSound(): void {
        console.log("The animal makes a sound");
    }
}

class Dog extends Animal {
    makeSound(): void {
        console.log("The dog barks");
    }
}

class Fish extends Animal {
    makeSound(): void {
        console.log("The fish makes no sound");
    }
}

function makeItSound(animal: Animal) {
    animal.makeSound();
}

const dog = new Dog();
const fish = new Fish();

makeItSound(dog);  
makeItSound(fish);
Enter fullscreen mode Exit fullscreen mode

I: Interface Segregation Principle (Principio de Segregación de la Interfaz)

Uno de los más importantes, este principio es clave para generar ese cambio y nos revela que no importa tener muchas interfaces. Si tienes varios clientes con distintos argumentos y comportamientos, crea interfaces que se ajusten a cada uno y no obligues a todos a tener una interfaz general con propiedades que no van a utilizar.

interface SportWithBall {
    ball: string;
}

interface SportWithPlayers {
    players: string[];
}

class Soccer implements SportWithBall, SportWithPlayers {
    players = ['Player1', 'Player2'];
    ball = 'Soccer ball';
}

class Swimming implements SportWithPlayers {
    players = ['Swimmer1', 'Swimmer2'];
}
Enter fullscreen mode Exit fullscreen mode

D: Dependency Inversion Principle (Principio de Inversión de Dependencias)

Llegamos al último principio, que claramente nos dice: “Las clases de alto nivel no deben depender de las clases de bajo nivel, sino de interfaces o abstracciones que definan lo que necesitan.” En el siguiente ejemplo se ve más claro.

interface NotificationService {
    send(message: string): void;
}

class EmailService implements NotificationService {
    send(message: string) {
        console.log("Sending email:", message);
    }
}

class SmsService implements NotificationService {
    send(message: string) {
        console.log("Sending SMS:", message);
    }
}

class Notification {
    private service: NotificationService;

    constructor(service: NotificationService) {
        this.service = service;
    }

    notify(message: string) {
        this.service.send(message);
    }
}

Enter fullscreen mode Exit fullscreen mode

Estas son las definiciones que yo le doy; quizá pueda estar mejor y ser más completo, pero hasta ahora, en el camino que estoy recorriendo, es lo que he entendido. Espero que estos ejemplos o la definición de los mismos principios puedan servir a alguien. Trato de documentar mi proceso y así evidenciar el avance que estoy teniendo. Muchas gracias por la lectura. ¡Feliz día! 😀💪🎉

Top comments (0)