La invención de los principios SOLID comenzó a finales de los años 80. Robert C. Martin comenzó a desarrollar estos principios mientras defendía el principio del diseño de software en USENET (un tipo temprano de Facebook). Después de la suma y la resta, Robert C. Martin formuló los principios a principios de la década de 2000. Fue hasta el año 2004 que se ordenaron los principios y se denominaron principios SÓLIDOS. Es un acrónimo que representa cinco principios de diseño específicos.
Los principios SOLID son útiles cuando se construyen módulos individuales o arquitecturas más grandes. Entonces, vamos a explorar cada principio junto con ejemplos en JavaScript.
Este principio fue descrito en el trabajo de Tom DeMarco¹ y Meilir Page-Jones². Lo llamaron cohesión. Definieron la cohesión como la relación funcional de los elementos de un módulo.
De hecho, este principio podría ser el menos entendido porque tiene un nombre particularmente inapropiado. Muchos desarrolladores entendieron que cada módulo debería hacer solo una cosa. No se equivoquen, hay un principio como ese. Pero no es uno de los principios SOLID y en realidad no es el SRP.
Se ha descrito de la siguiente manera: “ Cada módulo de software tiene una, y sólo una, razón para cambiar ”. Porque los sistemas de software se modifican para cumplir con las demandas de los usuarios, para satisfacer a las partes interesadas, por lo que podemos reformular el principio para decir esto: " Cada módulo debe ser responsable de uno, y solo uno, usuario o parte interesada " . Pero es probable que haya más de un usuario o parte interesada que quiera que el sistema cambie de la misma manera, los llamamos actor o grupo, por lo que la versión final es: “ Cada módulo debe ser responsable de uno, y solo uno ”. , actor ”.
Supongamos que tenemos un objeto Empleado, tiene tres funciones: calcularPago(), informarHoras() y guardar().
Desafortunadamente, está violando el SRP porque esas tres funciones están a cargo de tres actores diferentes.
Entonces, la forma de evitar este problema es separar el código que admite diferentes actores.
El objeto EmployData para guardar una estructura de datos simple compartida, utilizada por los tres actores.
El objeto PayCalculator tiene el
calculatePay()
método. El objeto HourReporter tiene el
reportHours()
método. El objeto EmployeeServer tiene el
save()
método. Así es como usamos el SRS para refactorizar códigos incorrectos. Cada función es responsable de un actor específico. El SRP es uno de los principios más simples y uno de los más difíciles de acertar.
Bertrand Meyer hizo famoso este principio en la década de 1980, que apareció en su libro Object-Oriented Software Construction³. Los sistemas de software se diseñen para permitir que se cambie el comportamiento de esos sistemas agregando un nuevo código, en lugar de cambiar el código existente.
El OCP establece lo siguiente: " Las entidades de software (clases, módulos, funciones, etc.) deben estar abiertas para la extensión, pero cerradas para la modificación" por Meyer, Bertrand . Este principio nos aconseja refactorizar el sistema para que más cambios de ese tipo no causen más modificaciones. La mayoría de los desarrolladores reconocen el OCP como un principio que los guía en el diseño de clases y módulos.
Hay dos atributos principales en el OCP, son.
Parecería que estos dos atributos están en desacuerdo porque la forma normal de extender el comportamiento de un módulo es hacer cambios en el código fuente de ese módulo.
Supongamos que cada empleado tiene un rol y privilegios otorgados. Pero, ¿qué pasa si introducimos un nuevo rol en el sistema y no modificamos las cosas existentes? Entonces, podemos hacer como el siguiente ejemplo para que pase el OCP.
Entonces, como en el ejemplo anterior, no tenemos que modificar el código existente, sino que podemos extenderlo para agregar un nuevo rol. El OCP es uno de los motores de la arquitectura de sistemas. El objetivo es hacer que el sistema sea fácil de extender sin incurrir en un alto impacto de cambio.
La famosa definición de subtipos de Barbara Liskov, de 1988 en un discurso de apertura de una conferencia titulado Data Abstraction and Hierarchy. En resumen, este principio dice que para construir sistemas de software a partir de partes intercambiables, esas partes deben adherirse a un contrato que permita que esas partes se sustituyan entre sí.
Uno de los ejemplos clásicos de este principio es un rectángulo que tiene cuatro lados. La altura de un rectángulo puede ser cualquier valor y el ancho puede ser cualquier valor. Un cuadrado es un rectángulo con igual ancho y alto. Entonces podemos decir que podemos extender las propiedades de la clase rectángulo a la clase cuadrada. Para hacer eso, debe intercambiar la clase secundaria (cuadrado) con la clase principal (rectángulo) para que se ajuste a la definición de un cuadrado que tiene cuatro lados iguales, pero una clase derivada no afecta el comportamiento de la clase principal, así que si lo hace que violará el principio de sustitución de Liskov.
Considere que tenemos una aplicación que usa un objeto rectángulo definido de la siguiente manera.
Basándonos en el conocimiento de que un cuadrado es un rectángulo cuyos lados tienen la misma longitud, decidimos crear un objeto cuadrado para usar en lugar de un rectángulo.
Desafortunadamente, se descubre un problema cuando la aplicación intenta usar nuestro cuadrado en lugar de un rectángulo. Resulta que uno de los métodos calcula el área del rectángulo así.
Cuando se invoca el método con un cuadrado, el producto es 16 en lugar del valor esperado de 12. Nuestro objeto cuadrado viola el principio de sustitución de Liskov con respecto a la función de área. En este caso, la presencia de las propiedades de largo y ancho fue una pista de que nuestro cuadrado podría no ser 100 % compatible con el rectángulo, pero no siempre tendremos pistas tan obvias.
Este principio aconseja a los diseñadores de software que eviten depender de cosas que no usan.
Supongamos que entras en un restaurante y eres vegetariano puro. El mesero en ese restaurante le dio la carta del menú que incluye artículos vegetarianos, artículos no vegetarianos, bebidas y dulces. En este caso, como cliente, debe tener una tarjeta de menú que incluya solo elementos vegetarianos, no todo lo que no come en su comida.
Aquí el menú debe ser diferente para diferentes tipos de clientes. La tarjeta de menú común o general para todos se puede dividir en varias tarjetas en lugar de solo una. El uso de este principio ayuda a reducir los efectos secundarios y la frecuencia de los cambios necesarios.
Porque no tenemos una interfaz por defecto en JavaScript. Pero todos nos habríamos enfrentado a situaciones en las que queremos hacer muchas cosas en el constructor de una clase. Entonces, ¿cómo implementar el ISP ahora?
Digamos algunas configuraciones que tenemos que hacer en el constructor. Las configuraciones que hacemos deben separarse de las otras configuraciones no deseadas en el constructor.
Aquí, la función validarUser() se invocará en la llamada del constructor initialUser() aunque no se necesite todo el tiempo. Podemos traer esto a ISP con el siguiente código.
Como el código anterior, estamos segregando la lógica no deseada de la función del contratista.
Antes de discutir este tema, tenga en cuenta que la inversión de dependencia y la inyección de dependencia son conceptos diferentes. La mayoría de las personas se confunden al respecto y consideran que ambos son iguales. Ahora los puntos clave están aquí para tener en cuenta acerca de este principio.
El Principio de Inversión de Dependencia (DIP) nos dice que los sistemas más flexibles son aquellos en los que las dependencias del código fuente se refieren solo a abstracciones, no a concreciones. Más bien, los detalles deberían depender de las políticas.
Puede considerar el ejemplo de la vida real de una batería remota de TV. Su control remoto necesita una batería, pero no depende de la marca de la batería. Puede usar cualquier marca XYZ que desee y funcionará. Entonces podemos decir que el control remoto del televisor está vagamente asociado con el nombre de la marca. La inversión de dependencia hace que su código sea más reutilizable.
En un lenguaje de tipo estático, como Java, esto significa que las declaraciones de uso, importación e inclusión deben referirse solo a los módulos fuente que contienen interfaces, clases abstractas o algún otro tipo de declaración abstracta. No se debe depender de nada concreto. En caso de JavaScript ¿Cómo podemos implementar el DIP?
Tomemos un ejemplo simple para entender cómo podemos hacerlo fácilmente.
Quiero contactar al servidor para algunos datos. Sin aplicar DIP, esto podría tener el siguiente aspecto.
Con DIP, podría escribir código como.
fillFromServer( "/address/to/data" , thingyView)
donde la abstracción
fillFromServer
La función puede, para el caso particular en el que queremos usar Ajax de jQuery, implementarse de la siguiente manera. La vista de abstracción se puede implementar para el caso particular de una vista basada en elementos con ID cosa1 y cosa2 de la siguiente manera.
Fácil, ¿verdad? Espero que esto le brinde una comprensión básica de cómo aplicar los principios SOLID en JavaScript.
Publicado anteriormente en https://medium.com/javascript-in-plain-english/rethinking-solid-principles-in-javascript-7effdd4dc37d