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.
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.
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.
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.
-
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.
-
Encapsulamiento: Es el proceso de proteger la información de un objeto para que no sea manipulada sin autorización.
-
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 comoSuperheroeConPoderes
, donde estaría Superman ySuperheroeSinPoderes
, donde estaría Ironman. -
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.
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:
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.
¿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.
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.
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.
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.
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.
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.
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.
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.
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.
¡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)
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 Ironman
y Cap
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.
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.
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.