Accede a todo EDteam con un único pago¡Sube a premium!

¿Qué son los principios SOLID en programación? La mejor explicación en español

Seguro has escuchado este término pero nadie te lo ha sabido explicar. No te preocupes, en este blog, lo entenderás de una vez por todas. Porque en español, #NadieExplicaMejor que EDteam.

Diseño web
8 minutos
Hace 9 meses
¿Qué son los principios SOLID en programación?  La mejor explicación en español

Aprender a programar es fácil: variables tipos de datos, operadores, condicionales, ciclos. Son como las matemáticas de colegio. Sin embargo, escribir código limpio, con buenas prácticas, mantenible y escalable es otra cosa.

Por eso, las buenas prácticas son una de las principales diferencias entre un junior y un senior. El junior busca que el código funcione, el senior no solo se enfoca en eso, sino que también piensa en que sea mantenible en el futuro.

Y entre los recursos más importantes para escribir código limpio están los principios SOLID. Si has escuchado el término, pero nadie te lo explico bien, este es el blog que buscabas, porque en español, #NadieExplicaMejor que EDteam.

Conceptos básicos

Para entender los principios SOLID, primero necesitamos entender algunos conceptos previos.

1. 1. Programación Orientada a Objetos

Es el paradigma de programación más utilizado, empezó en los años 70 y se ha extendido a casi todos los lenguajes. Este paradigma usa objetos que se comunican entre ellos para representar las entidades del programa. Por ejemplo, en EDteam, los usuarios, cursos, suscripciones premium y profesores son objetos.

Frame 2.png

Los objetos tienen atributos (también llamados propiedades) que son sus características, y métodos, que son sus funciones. Por ejemplo, como atributos, un usuario tiene su nombre y su correo. Y como métodos, sería iniciar sesión o comprar una suscripción premium.

Frame 3 (1).png

Si quieres entender más a fondo la Programación Orientada a Objetos, tenemos la mejor explicación en español que puedes ver en este video. No solo eso, ¿quieres aprender más y obtener certificado por tus nuevos conocimientos? Toma nuestro curso: Introducción a la Programación Orientada a Objetos. ¡Es completamente gratis!

1. 2. Clases

Estos objetos se crean a partir de plantillas llamas clases. Estas clases son como el plano de una casa, a partir del cual se pueden construir muchas casas. Por ejemplo, podríamos tener una clase “superhéroe” que tenga los métodos volar y superfuerza. Y a partir de esa clase, crear cada superhéroe que sería un objeto.

Frame 4.png

La creación de un objeto a partir de una clase se llama instanciación, y cada objeto es una instancia de la clase.

1. 3. Pilares de la Programación Orientada a Objetos

Son los conceptos fundamentales que debes saber para comprender este paradigma.

edteam-blog.png

  1. Abstracción: Consiste en extraer las propiedades más importantes de un objeto del mundo real para llevarlo a una clase. Por ejemplo, en el caso de los superhéroes, extrajimos la capacidad de volar y la superfuerza.

  2. Encapsulamiento: Es el proceso de proteger la información de un objeto para que no sea manipulada sin autorización.

  3. Herencia: Una clase hija hereda atributos y métodos de una clase padre. Por ejemplo, a partir de la clase Superheroe, podemos crear clases hijas como SuperheroeConPoderes, donde estaría Superman y SuperheroeSinPoderes, donde estaría Ironman.

    Frame 5.png

  4. Polimorfismo. Significa que objetos de clases diferentes pueden tener el mismo comportamiento (o sea, los mismos métodos) pero implementarlos de diferentes maneras. Por ejemplo, Superman e Ironman pueden volar, pero de formas muy diferentes.

2. Interfaces y clientes

Otro concepto que debes comprender antes de entrar a SOLID, son las interfaces y los clientes.

Las interfaces están muy relacionadas con el polimorfismo. ¿Te acuerdas de que Superman y Ironman pueden volar, pero de formas diferentes? De ahí viene polimorfismo: varias formas de hacer lo mismo.

Pero este polimorfismo trae el riesgo de tener código inconsistente: la forma de volar de Ironman con una estructura de código, nombre y argumentos diferentes a la de Superman. Y si luego creamos otro superhéroe volador, crearemos otra estructura para que vuele, complicando cada vez más el código.

Para evitarlo, usamos las interfaces, que son contratos que definen el nombre de los métodos y los argumentos que reciben; pero no especifican como implementar el método. Ese es problema de cada clase. Un ejemplo sencillo es el timón de un auto. Si lo giras a la derecha, el auto gira a la derecha. Cada fabricante de autos puede tener su propia tecnología de conducción, pero deben cumplir el contrato de darle al conductor un timón.

En el caso de los superhéroes, creamos una interfaz Volador que Superman e Ironman deben respetar. Como en el ejemplo del timón del auto, a la interfaz no le interesa como vuela Superman ni como vuela Ironman, solo que respeten el contrato.

Frame 5.png

Por último, tenemos el concepto de clientes, que son partes del programa que interactúan o utilizan clases o interfaces. En el ejemplo del auto, el conductor sería el cliente de la interfaz timón. Y en nuestro ejemplo de superhéroes voladores, las clases SuperheroeConPoderes y SuperheroeSinPoderes son clientes de la interfaz Volador mientras que Superman y Ironman son clientes de las clases SuperheroeConPoderes y SuperheroeSinPoderes.

Principios SOLID en la programación

Ahora ya estamos listos para comprender los principios SOLID. Como te dije al inicio de este blog, los principios SOLID nos ayudan a escribir código limpio, mantenible y escalable. Un libro muy famoso para aprender a escribir código limpio es Clean Code de Robert C Martin, que todo programador debería leer.

Ten en cuenta que los principios para escribir código limpio no se limitan a SOLID, sino que hay muchos más que podremos ver en otro blog. En el caso específico de SOLID, la palabra SOLID en realidad no significa que tu código sea sólido o robusto (como podrías pensar) sino que es solo un acrónimo que nos ayuda a recordar los 5 principios.

  • S: Principio de responsabilidad única (Single Responsibility Principle - SRP).
  • O: Principio de Abierto / Cerrado (Open/Closed Principle - OCP).
  • L: Principio de sustitución de Liskov (Liskov Substitution Principle - LSP).
  • I: Principio de segregación de interfaces (Interface Segregation Principle - ISP).
  • D: Principio de inversión de dependencias (Dependency Inversion Principle - DIP).

Cuatro de estos principios fueron desarrollados por Robert C Martin, (el mismo de Clean Code), ingeniero de software que recibió el apodo cariñoso de tío Bob (uncle Bob) por la comunidad de desarrollo de software, por su figura de mentor y guía. Mientras que el tercero fue desarrollado por Bárbara Liskov, una reconocida ingeniera de Software norteamericana.

Ten en cuenta que en este blog te explicaré los principios SOLID de la forma más didáctica posible y si quieres ir más a fondo y aplicar SOLID en un proyecto real, tenemos el curso de SOLID y Arquitectura Hexagonal en EDteam. ¡Las primeras clases son gratis!

1. S: Responsabilidad única

Una clase debe tener una única responsabilidad, es decir, una única función, y por ende, dicha clase debe tener solo una razón para modificarse.

Supongamos que tenemos una clase Superheroe, como sabes, los superhéroes tienen habilidades y características muy diferentes, así que podríamos pensar en una clase de este tipo:

Solid y arquitectura hexagonal (3).png

En la que agregamos métodos como volar, que es una habilidad que tienen casi todos los superhéroes (pero no todos), luego trepar paredes que solo lo tiene spiderman, luego lanzar fuego y así. Esta clase está haciendo demasiadas cosas y tiene demasiadas razones para cambiar, lo que va a originar problemas cuando el código crezca.

La manera de resolver este problema es que la clase superhéroe tenga una sola función, por ejemplo, realizar superhabilidades, y que las habilidades individuales sean delegadas a otras clases.

Solid-arquitectura-hexagonal-blog-EDteam.png

¿Y cómo hago para traerme las habilidades que están en otras clases? Lo veremos en un momento.

O: Abierto / Cerrado

Este principio está muy relacionado con el anterior porque dice que una clase debe estar cerrada para su modificación y abierta para su extensión. Es decir, que si quieres agregar nuevas funcionalidades a la clase no deberías cambiar el código (porque podrías romper cosas) sino extender esas funcionalidades desde afuera.

Usemos el ejemplo anterior: tenemos la clase superhéroe con el método realizarSuperHabilidades, a partir de esa clase instanciamos el objeto Superman.

Solid y arquitectura hexagonal (5).png

Pero, ¿cuál es la principal habilidad de Superman? Volar. Entonces, ¿agregamos volar a la clase Superhéroe? No, porque violaría el principio de abierto / cerrado.

Solid y arquitectura hexagonal (5).png

La forma correcta sería tener una interfaz superhabilidad y diferentes clases para cada habilidad. Así, cada habilidad debe cumplir el contrato de la interfaz y cada vez que creamos un nuevo superhéroe le asignamos sus habilidades.

Solid y arquitectura hexagonal (7).png

L: Sustitución de Liskov

Este principio dice que si tenemos clases padre y clases hijas (es decir, herencia), las instancias de las clases hijas y las instancias de las clases padres deben poderse intercambiar sin producir resultados inesperados.

Por ejemplo, si tenemos la clase Superhéroe volador que hereda de la clase superhéroe, los objetos instanciados a partir de esas dos clases deberían ser también superhéroes, obvio. Así que podrían intercambiarse.

Entonces, supongamos que de Superhéroe instancio a Ironman y de Superhéroe volador a Superman, ambos podrían intercambiarse sin problemas, los dos son superhéroes y los dos vuelan.

Solid y arquitectura hexagonal (8).png

Ahora supongamos que en la subclase Superheroe volador agrego una restricción que dice que solo puede volar con sus superpoderes. En ese caso, Ironman, que vuela con un traje, no cumpliría con esa restricción y los objetos ya no serían intercambiables. Habríamos roto el principio.

Solid y arquitectura hexagonal (9).png

Esto quiere decir que debemos evitar agregar restricciones o cambiar el comportamiento de los métodos heredados en una clase para no tener luego comportamientos inesperados en la aplicación.

I: Segregación de interfaces

Este principio dice que los clientes no deben verse obligados a depender de interfaces que no utilizan. Es decir, que un cliente solo debe conocer los métodos que va a utilizar.

Volvamos al ejemplo de la clase superhéroe. Por el principio de responsabilidad única sabemos que no debemos meter todas las habilidades ahí, sino crear una interfaz.

Entonces creamos la interfaz HabilidadesDelSuperHeroe y agregamos habilidades como volar, trepar muros, lanzar fuego, etc.

Solid y arquitectura hexagonal (10).png

Esto parece buena idea, pero supongamos que creamos a Superman a partir de la clase superhéroe. Según este diseño, Superman treparía muros y lanzaría fuego y sabemos que no es así, por lo tanto, hemos roto el principio de segregación de interfaces.

Solid y arquitectura hexagonal (11).png

Lo que podemos hacer es tener una clase Superheroe con todos los métodos y propiedades comunes a los superhéroes. Luego, con herencia, creamos clases hijas para cada tipo de superhéroe.

Solid y arquitectura hexagonal (12).png

Como cada tipo de superhéroe tiene habilidades diferentes, creamos interfaces específicas para cada habilidad. Entonces el superhéroe trepamuros tiene superfuerza y trepa muros, y el superheroe volador puede volar y tiene superfuerza.

Solid y arquitectura hexagonal (13).png

¡Ahora sí hemos segregado las interfaces! Y podemos instanciar superhéroes específicos que tendrán sus superpoderes específicos (ahora Superman ya no trepa muros)

Solid y arquitectura hexagonal (13).png

D: Inversión de dependencias

Este principio dice que las clases de alto nivel no deben depender de las de bajo nivel, sino que ambas deben depender de abstracciones. Y que los detalles deben depender de las abstracciones, y no al revés.

Para entender esto que suena un poco a trabalenguas imaginemos la clase TheAvengers que es de alto nivel porque requiere a las clases Ironman y Cap que son las de bajo nivel.

Lo que nuestro sentido común nos diría es que dentro de la clase TheAvengers llamemos a las clases Ironmany Cap

Solid y arquitectura hexagonal (15).png

Pero esto sería un error, ya que si luego queremos agregar a Hulk tendríamos que modificar la clase TheAvengers y esto viola el principio de abierto y cerrado. ¿Te acuerdas? Cerrado para su modificación pero abierto para su extensión.

Solid y arquitectura hexagonal (16).png

Lo que haremos es usar una interfaz Avenger que tiene el contrato que deben seguir todos los avengers. Y por su lado, la clase TheAvengers recibe las instancias de los superhéroes a través de la interfaz. Por eso a esta técnica se le conoce como inyección de dependencias, ya que la clase, en vez de traer las dependencias, las recibe desde afuera.

Solid y arquitectura hexagonal (16).png

Ahora ya entiendes qué son los principios SOLID y como funcionan. Recuerda que si quieres ir más a fondo, escribiendo tu código y creando un proyecto real con estos principios, toma nuestro curso SOLID y Arquitectura Hexagonal en EDteam. Es un paso obligatorio en tu camino a programador senior.

Y si no sabías qué son los principios SOLID, #LoAprendisteEnEDteam.

Comentarios de los usuarios

Pregunta a ChatEDT