De POO a SOLID explicado con Pokémon - Los cinco principios de SOLID
Tags: oop, javascript, solid
Published: 2020-09-06 09:17:58


###### GUÍA PARA ENTENDER LOS CINCO PRINCIPIOS DE SOLID.
#### JavaScript — Los cinco principios de SOLID

Genial estamos por terminar nuestra aventura **Pokémon**, finalmente veremos los cinco principios de **SOLID**, para poder llegar hasta este punto fue necesario pasar los siguientes retos.

📕 El paradigma de la Programación Orientada a Objetos
📗 Los cuatro principios de la Programación Orientada a Objetos

Si seguiste el tema desde el primer artículo deja decirte gracias 👏, regresando al tema que nos compete y posible mente la pregunta que te estés realizando al igual que yo es, pero **¿Que son los cinco principios de SOLID?**

>📝 Nota: Para entender los principios de SOLID es recomendable tener bases de los temas anteriores.

***

### Pero, 🤔 ¿Qué son los principios de SOLID?

Es una serie de cinco reglas o principios que son aplicados en la **POO** cuyas iniciales dan como resultado el acrónimo **SOLID** este nombre es definido por [Michael Feathers](https://www.linkedin.com/in/michaelfeathers/) el cual hace referencia a los principios definidos por [Robert C. Martín](https://es.wikipedia.org/wiki/Robert_C._Martin) (**Tío Bob**) y [Barbara Liskov](https://es.wikipedia.org/wiki/Barbara_Liskov).

Aplicar y conocer dichos principios tiene como consecuencia un desarrollo.

![Fig. 1: Beneficios de SOLID.](https://cdn-images-1.medium.com/max/2720/1*YPdCb1_YqyrRiqV2PLI8Ew.png)

En términos más generales se puede conseguir un mejor diseño de arquitectura y un código de mayor calidad, cada una de las letras de **SOLID** hace referencia a uno de sus principios.

![Fig.2: El acrónimo de SOLID.](https://cdn-images-1.medium.com/max/3608/1*N8YzTFRrvoSaWlnMqv9-_g.png)

Los principios de **SOLID** no solo son aplicables a **POO** también son aplicables en **Funciones** (Programación funcional), pero es muy común verlo aplicado en **POO**, Estos principios son aplicables a cualquier lenguaje de programación.

> 🔖 **Nota**: Todos los ejemplos de código utilizados están basados en **JavaScript** usando las especificaciones más actuales hasta el momento.

***

### 📕S: Single Responsability Principle / Principio de Responsabilidad Única

![Fig. 3: Single responsibility principle o Principio de responsabilidad única.](https://cdn-images-1.medium.com/max/5128/1*KMmxj_npKkze19bAdLfnfQ.png)

Una **Clase** solo debe tener solo una razón para cambiar, esto quiere decir que una clase solo debe contar con una sola responsabilidad.

Si una **Clase** contara con múltiples responsabilidades esto puede implicar que al realizar un cambio de alguna de ellas puede tener como consecuencia la modificación de otras responsabilidades lo que aumenta la posibilidad de incluir errores y poder afectar otras partes del sistema sin saberlo.
> **🔖 Nota**: **Single Responsability Principle **también** **se conoce como** SRP** por sus siglas en ingles.

Interesante, pero generemos un ejemplo utilizando la vieja confiable **[Clase Pokémon](https://gist.github.com/konami12/2359d276454cdcb81d291597a042e375).**

{% gist https://gist.github.com/konami12/afe21369ed1f62333fefb6c0cdb34bb0 %}

Como podemos apreciar el ejemplo anterior no está aplica **SRP** ya que la **Clase **consta de más de una **responsabilidad**.

![Fig. 5: Problemas de la clase Pokémon.](https://cdn-images-1.medium.com/max/2720/1*ITVAfF8pOpmFFukKURHr2A.png)

Al contar con múltiples **responsabilidades** se complica aplicar cambios ya que es posible que insertemos un **error** porque hacer un cambio de alguna responsabilidad, podría afectar a otras sin que nosotros lo sepamos, es momento de aplicar **SRP.**

{% gist https://gist.github.com/konami12/3039456df210421462f07934cdd269bd %}

Aplicando **SRP** vemos que entra en juego una nueva clase llamada **DataBase**la cual es encarga de manipular la Base de datos y por otro lado la **[Clase Pokemon](https://gist.github.com/konami12/3039456df210421462f07934cdd269bd)**solo se encarga de definir nuevos **Pokémons**, de esta manera cada **Clase**tiene una responsabilidad además podemos conseguir una **alta cohesión**.

> # alta cohesión: Se refiere a la media que módulos de un sistema tiene una sola responsabilidad.

***

### 📗O: Open-Closed Principle/ Principio de Abierto-Cerrado

![Fig. 7: Open-closed principle/ Principio de abierto-cerrado](https://cdn-images-1.medium.com/max/5128/1*3vcBbZSjRpKdwr_8QJcjOg.png)

Una entidad de **SoftWare** tiene que estar abierta para su extensión, pero cerrada para su modificación. Lo que establece este principio es que siempre que se desee realizar un cambio o nueva característica, se tendría que agregar código nuevo en lugar de modificar el existente.

Si una se desea que una **Clase** realice más funciones, lo ideal es extender las funcionalidades ya existentes y no modificarlas.

> **🔖 Nota**: **Open Closed Principle** también se conoce como **OCP** por sus siglas en ingles.

Nuevamente utilizaremos la vieja confiable **[Clase Pokémon](https://gist.github.com/konami12/2359d276454cdcb81d291597a042e375)**, para generar el ejemplo.

{% gist https://gist.github.com/konami12/351ab54bad02db6515bc140c0f9b89af %}

Como podemos apreciar el ejemplo anterior no está aplica **OCP** ya que la **Clase ProcessAtack** está abierta para recibir nuevos cambios.

![Fig. 8: Problemas de la clase ProcessAttack.](https://cdn-images-1.medium.com/max/2720/1*-vOMQFMDWMruF7koEE04Pw.png)

Al momento de que nuestra la **Clase** está abierta a recibir nuevos cambios es posible que insertemos un **error**, porque estaríamos modificando código ya existente, para aplicar **OCP** utilizaremos el principio de **Herencia y **Polimorfismo.**

{% gist https://gist.github.com/konami12/e1220fc37407e5f410689cd9e2388b86 %}

Al aplicar **OCP** en la **Clase ProcessAttack** lo primero que vemos es que ya no le importa saber el tipo de **Pokémon** solo le interesa el resultado del método **attack** para poder realizar la acción de ataque, con este nuevo diseño para poder agregar nuevos ataques por tipo de **Pokémon** solo es necesario crear nuevas **SubClases** de la **Clase Pokémon**, esto es un diseño mucho más robusto y fácil de extender.

***

### 📘 L: Liskov Substitution Principle/Principio de Sustitución de Liskov

![Fig. 10: Liskov substitution principle / Principio de sustitución de Liskov](https://cdn-images-1.medium.com/max/6120/1*7goesHL3-wygzxMaAoo7rQ.png)

Puede que por su nombre asuste un poco 😱, pero en realidad es más sencillo de lo que parece. Este principio lo que dice es, Si **S** es un subtipo de **T**, los objetos de tipo **T** en un programa pueden reemplazarse por objetos de tipo** S** sin alterar ninguna de las propiedades del programa.

> # Este …ehm… como te digo que eso no suena tan sencillo como lo imaginaba.

De una manera más simple, el principio declara es que una, **SubClase** (**clase hija**) debe ser sustituible por su **Super Clase** (**clase padre**), si al hacer esto la clase falla estamos violando el principio🤯.
> **🔖 Nota**: **Liskov Substitution Principle** también se conoce como **LSP** por sus siglas en ingles.

Nuevamente utilizaremos la vieja confiable **[Clase Pokémon](https://gist.github.com/konami12/2359d276454cdcb81d291597a042e375)**, para generar el ejemplo.

{% gist https://gist.github.com/konami12/f23c963627e8b2466a6ecb1e30138328 %}

El ejemplo anterior está rompiendo el principio **LSP** ya que como podemos apreciar la **Sub-Clase** (**Charmander**) tiene un comportamiento que difiere de la **Clase-Padre** (**Pokémon**),

![Fig. 12: Problemas de la Clase Charmander](https://cdn-images-1.medium.com/max/2720/1*aFeOrTAXPZdIQp3wKmECcQ.png)

Al momento que una **Sub-Clase** no puede realizar las mismas acciones que la **Clase-Padre** esto puede provocar errores, para poder aplicar **LSP**utilizaremos el principio de **Herencia.**

{% gist https://gist.github.com/konami12/70f148044841bdb039740e93c295fc85 %}

Al aplicar **LSP** entra en juego **PokemonFly** que hereda de **Pokémon** y tiene el método **canFly,** de esta manera podemos definir quién puede volar y quien no, este principio es una advertencia de que el polimorfismo es poderoso, pero no siempre es fácil de aplica correctamente.

***

### 📙 I: Interface Segregation Principle/ Principio de Segregación de Interfaces

![Fig. 13: Interface Segregation Principle/ Principio de Segregación de Interfaces](https://cdn-images-1.medium.com/max/5304/1*Mznwp9wgDpGvOsSS8WQBoA.png)

Los **clientes** no tienen que verse forzados a depender de interfaces que no utilicen, en otras palabras, cuando un **Cliente A** depende de una **Clase** que implementa una **interfaz** cuya funcionalidad el **Cliente A** no utilice, pero otros si, él **Cliente A** estará siendo afectados por los cambios que fuercen otros clientes.

Este principio suena muy similar a **SPR** ya que ambos están centrados en la **cohesión** de **responsabilidades.**

> # Este …ehm… pero en JavaScript no existen las interfaces genio.

Por lo que este principio no es aplicable estrictamente como otros, lo ideal es implementar pequeñas **interfaces simuladas.**

> **🔖 Nota**: **Interface Segregation Principle** también se conoce como **ISP** por sus siglas en ingles.

Nuevamente utilizaremos la vieja confiable **[Clase Pokémon](https://gist.github.com/konami12/2359d276454cdcb81d291597a042e375)**, para generar el ejemplo.

{% gist https://gist.github.com/konami12/3aa606f6a86e42aec3128db04a6d6191 %}

Como podemos apreciar el ejemplo anterior no está aplica **ISP** ya que la **Clase Pokemon** tiene métodos que no son aplicables en todos las **SubClases**las cuales se ven obligadas aplicar acepciones o comportamientos diferentes para los métodos que no utilizan.

![Fig: 15: Problemas de la clase Pokémon.](https://cdn-images-1.medium.com/max/2720/1*G0EpAk7cn8_A8syjZU5MrA.png)

Al momento de que nuestra la **Clase** consta de métodos que pueden o no aplicar a sus descendientes es muy fácil que insertemos **errores**, la solución para poder implementar **ISP** es necesario separar el código en pequeñas partes así de cada clase podrá usar los métodos que realmente utilice.

> **🔖 Nota**: En **JavaScript** solo se puede **tener** una **clase** **padre**, por lo que la **herencia** **múltiple** de **manera directa no es posible**, **para** eso usaremos los **[mix-ins](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Classes#Mix-ins).**

{% gist https://gist.github.com/konami12/0af1ca95d31504685b04736e41daed4c %}

Al aplicar **ISP** entran en juego el manejo de **interfaces**, pero como sabemos en **JavaScript** por lo que implementamos **Mix-ins** con los cuales podremos simular un comportamiento parecido a las interfaces con lo que podremos agregar solo los métodos que realmente necesite nuestra **Subclase.**

***

### 📒 D: Dependency Inversion Principle / Principio de Inversión de Dependencia

![Fig., 17:Dependency Inversion Principle / Principio de Inversión de Dependencia](https://cdn-images-1.medium.com/max/5120/1*5FvvdM9QuyeLaBSIN55HjQ.png)

Realmente este principio dicta dos puntos importantes los cuales son

> #### Los módulos de alto nivel no deben depender de los módulos de bajo nivel, Ambos tiene que depender de abstracciones.

> #### Las abstracciones no deben depender de los detalles. Los detalles deben depender de abstracciones.

Hay un momento puede que esto no suene tan sencillo inicialmente, pongamos en claro los términos utilizados.

- **Módulo de Alto Nivel (Clase):** **Clase** con la que se ejecuta una acción utilizando una **herramienta**

- **Módulo de Bajo Nivel (Clase): La **herramienta** necesaria para ejecutar la acción

- **Abstracción:** Representa la interfaz que conecta a las 2 clases

- **Detalles:** Cómo funciona la herramienta.

> **🔖 Nota**: **Dependency Inversion Principle** tambiénse conoce como **DIP** por sus siglas en ingles.

En este ejemplo crearemos una clase llamada **Pokedex** ya que desde mi punto de vista es como el mejor ejemplo que se presta para la explicación.

{% gist https://gist.github.com/konami12/fc3ffd0c0fc676361f5136e486c8dee6 %}

Revisando el ejemplo podemos ver que la **Clase Pokedex** tiene una dependencia directa a **ApiPokemon.** por esta razón el principio de **DIP** no se aplica ya que una de las **Clases** tiene conocimiento de cómo se implementa **ApiPokemon.**

![Fig. 19: Problemas de la clase Pokedex.](https://cdn-images-1.medium.com/max/2720/1*uwUD8CFps1J6I0ZqrJtKIg.png)

Para poder implementar **DIP** utilizaremos inyección de dependencias así la **Clase Pokedex** solo se encargará de solicitar data.

{% gist https://gist.github.com/konami12/7386a4eaefc550c5728c5300de34487b %}

Al momento de realizar una inyección de **Dependencias** la clase Pokedex, eliminamos la dependencia que se tenía de la clase **ApiPokemon**, de esta manera cumpliríamos con el principio de **DIP.**

> 🔖 Nota: La implementación de **DIP** regular mente puedes se utiliza con interfaces en este caso podríamos haber usado mix-in.

***

### Conclusiones

Como podemos ver cada uno de los principios de **SOLID** consigue un objetivo en concreto.

- **Principio de Responsabilidad Única:** 
Su propósito es separar los comportamientos.

- **Principio Abierto/Cerrado:** 
Su objetivo es ampliar el comportamiento de una clase sin modificar el comportamiento existente.

- **Principio de Sustitución de Liskov:**
**Su objetivo es aplicar coherencia entre clases.

- **Principio de Segregación de la Interfaz:**
**Su objetivo es dividir un conjunto de acciones en conjuntos más pequeños para ejecutar solo el conjunto de acciones que se requieren.

- **Principio de inversiones Dependencias:**
**Su objetivo es reducir la dependencia de una clase de alto nivel en la clase de bajo nivel mediante la introducción de una interfaz.

Por último, recordemos que **SOLID** solo es una herramienta que nos ayuda a escribir mejor código, por lo que hay que tomar en cuenta que no hay que caer en el uso excesivo de **SOLID** ya que puede que estos principios compliquen mucho el código si es así talvez solo sea necesario aplicar parte de estos.