miércoles, 14 de noviembre de 2018

Principio de Inversión de Dependencia

Es un principio estructural, que habla que los módulos de alto nivel no deben depender de los módulos de bajo nivel, en otras palabras las dependencias del código se refieren solo a abstracciones, no a concreciones. El seguir este principio nos da el control sobre la dirección de todas las dependencias.

Para ilustrar lo anterior miremos la siguiente imagen.


En esta imagen vemos dos componentes uno es el de las vistas y otro es el de los controladores y como se observa un controlador necesita una instancia de la vista para poder indicar que acción realizar, pero esto presenta varios problemas, por ejemplo, no queremos que un cambio en la Vista1 nos obligue a volver a compilar o modificar nuestra clase Controlador1, para esto podemos usar la inversión de dependencia como veremos en la siguiente imagen.


Al crear una interfaz en el componente de los controladores que abstrae el comportamiento de una vista podemos invertir la dependencia, ahora el componente de las vistas depende del componente de los controladores, de esta forma protegemos al componente de los controladores de los cambios en componente de las vistas.

Que tal si vemos un ejemplo más concreto para ver esto.

Imaginemos estamos viendo el espectáculo de Gepetto y Pinocho y decidimos recrearlo en una pequeña aplicación.



Para empezar con nuestra aplicación debemos hacernos un par de preguntas.
¿Quien conoce como manipular un títere?, ¿Quien realiza la acción?. Es claro que quien sabe como manipular un títere es Gepetto y quien realiza la acción es Pinocho, solo con estas dos simples preguntas ya podemos hacernos una idea quien tiene las funciones de alto nivel y quien las de bajo nivel.

Gepetto contiene las funciones de alto nivel (Es quien conoce las reglas del negocio y sabe como manipular un titere), cuales serian estas funciones.

  • void mostrarTitereInmovil();
  • void moverCabezaDelTitere();
  • void moverBrazoIzquierdoDelTitere();
  • void moverBrazoDerechoDelTitere();
  • void moverPiernaIzquierdoDelTitere();
  • void moverPiernaDerechoDelTitere();

Pinocho contiene las funciones de bajo nivel (Es quien simplemente realiza las acciones que Gepetto sabiamente le ordena), cuales serian estas funciones.

  • void inmovil();
  • void moverCabeza();
  • void moverBrazoIzquierdo();
  • void moverBrazoDerecho();
  • void moverPiernaIzquierdo();
  • void moverPiernaDerecho();


Para ilustrar esto tendremos dos componentes uno de alto nivel (donde estará Gepetto) y otra de bajo nivel (Donde estará Pinocho). A estas alturas es obvio que Gepetto debe tener una instancia de Pinocho para poder manipularlo por lo que el diagrama se vería algo así.


Es evidente a simple vista que el componente de alto nivel depende del componente de bajo nivel lo cual es un error, imaginemos que Pinocho sale a vacaciones y viene en su reemplazo Pepito Grillo, ¿Qué pasaría?, ¿Tendríamos que hacer cambios en nuestra aplicación?, es obvio que la respuesta es si, primero Gepetto no debería estar condicionado a trabajar con un solo títere, él es un titiritero y sabe manipular a cualquier títere que le pongan, por lo cual empiezo a evidenciar un problema de abstracción, una ultima consideración seria, y que tal que él que salga a vacaciones es Gepetto, podríamos crear un nuevo titiritero para Pinocho, es evidente que con el diseño actual no podríamos, por lo que vamos a hacer una mejor abstracción y mejorar considerablemente nuestro diseño e invirtiendo la dependencia.

Creo que ya nos dimos cuenta que los conceptos principales del dominio de la aplicación son Titiritero y Títere por lo que creo que deberíamos abstraer su comportamiento creando dos Interfaces y que estas contengan la declaración de las funciones ya mencionadas.

Como segundo paso Gepetto debería implementar la interfaz Titiritero porque es claro que él es un titiritero.

El tercer paso consiste en que Pinocho implemente la interfaz Titere.

Ahora Gepetto no debe tener una instancia de Pinocho sino declara una variable de clase de tipo Titere y es necesario entonces que los titiriteros tengan una función adicional setTitere(Titere titere) tengamos presente que en un espectáculo de títeres un titiritero puede cambiar contantemente de títere y creo que esta función le caería de mil maravillas.

Por ultimo viene la pregunta crucial donde deben ir estas interfaces porque de dicha ubicación dependen si se logra la inversión de dependencia o no.  Imagino que ya tienes la respuesta, si así es, deben ir en el componente de alto nivel, nuestro nuevo diagrama queda así.


Si comparas los dos diagramas ahora la flecha de dependencia se ha invertido y el componente de bajo nivel depende del componente de alto nivel y nuestra aplicación ahora puede crecer de una forma mas flexible, puedes crear más títeres y mas titiriteros si así lo deseas.

El código fuente de este ejemplo en encuentras en mi repositorio de GitHub dedicado a la Programación Orientada a Objetos

Si deseas ampliar más este tema te recomiendo el artículo The Dependency Inversion Principle de Robert C. Martin.

Si deseas leer sobre los demás principios SOLID da click aquí.

No hay comentarios:

Publicar un comentario

Instalación NodeJS

Ingresamos a la página oficial de NodeJS donde lo descargaremos  https://nodejs.org/en/download/ Escogemos el instalador que se ajuste a ...