Introducción a la Inversión de Control

26·Feb·2014

|

¿Qué es?

La inversión de control o Principio de Hollywood, es un método de programación en el que el flujo de la ejecución de un programa se invierte.

Así, la interacción se expresa especificando las respuestas deseadas a sucesos o solicitudes de datos concretas, dejando que algún tipo de entidad o arquitectura externa lleva a cabo las acciones de control, con lo que no es necesario que el programador especifique la secuencia de decisiones y procedimientos mediante las llamadas a funciones.

Es el principio subyacente de la Inyección de Dependencias, del cual hablamos en artículos anteriores.

¿Qué problemática puede solucionar?

La mejor manera de ver el funcionamiento de la inversión de control es planteando una problemática y viendo cómo este método de programación puede ayudarnos a solucionarla.

Así, planteamos el caso siguiente:

En algunas situaciones, nos encontramos con que tenemos clases que tienen dependencias con otras clases o con componentes en los que sus implementaciones se especifican en tiempo de diseño.

En el ejemplo que vemos a continuación tenemos una clase ClassA que tiene dependencias con los servicios ServiceA y ServiceB.

alt text

Este diseño tiene los siguientes problemas:

  • Remplazar o modificar las dependencias requiere cambiar el código de ClassA

  • Las implementaciones de las dependencias tienen que estar disponibles en tiempo de compilación.

  • La clase ClassA es difícil de aislar para poder ejecutar tests, ya que se tienen referencias directas a las dependencias. Significa que estas dependencias no se pueden falsear.

  • La clase ClassA necesita conocer y administrar las dependencias que tiene.

Utilizando la Inversion de Control obtenemos:

  • Desacople de la clase ClassA de sus dependencias, de tal manera que puedas remplazar y/o modificar las dependencias sin repercutir en la clase ClassA.

  • Escribir clases que dependan de servicios cuyas implementaciones no estén en tiempo de compilación.

  • Testear la clase ClassA sin usar realmente las dependencias. Poder aislarla de sus dependencias.

  • Hacer que la clase ClassA no se responsabilice de la localización y administración de sus dependencias.

Con la Inversión de Control delegamos la responsabilidad de seleccionar las implementaciones de las dependencias a un componente externo.

Tenemos dos patrones de Inversion de Control:

ServiceLocator como patrón de Inversión de control

En este patrón se crea un componente llamado ServiceLocator (buscador de servicios) que es el encargado de devolver las implementaciones de las dependencias. ServiceLocator es un patrón de diseño bastante común que permite desacoplar los clientes de servicios de las implementaciones de estos servicios.

El diagrama es el siguiente:

alt text

Vemos que la clase ClassA usa ServiceLocator para localizar las dependencias ServiceA y ServiceB. Por lo tanto, la responsabilidad de conocer cómo crear las dependencias ya no recae en la clase ClassA.

Ejemplo

Veamos un ejemplo de ServiceLocator y su uso:

  • Primero definimos el contrato del Service Locator

alt text

  • Ahora vemos una implementación sencilla del contrato anterior:

alt text

  • Ahora vemos como una clase obtiene sus dependencias con ServiceLocator:

alt text

Como podemos comprobar MyServiceOne no conoce la manera de resolver su dependencia IMyServiceTwo. MyServiceOne solo tiene que interactuar con ServiceLocator para obtener una implementación.

Este es solo un ejemplo sencillo de cómo implementar el patrón ServiceLocator. Existen muchas maneras de mejorar la implementación que hemos realizado. Por ejemplo, ServiceLocator puede crearse como Singleton, ya que no tiene sentido tener más de una instancia de ServiceLocator.

También podemos comprobar que en el momento de inicializar ServiceLocator se crea una instancia de todos los servicios que hayamos registrado; una posible mejora sería hacer que los servicios se instanciaran en el momento en que alguien los quiera utilizar (lo que se conoce como Lazy initialization).

Inyección de dependencias por constructor como patrón de diseño

Como comentamos en el anterior artículo, la Inyección de Dependencias es un patrón de diseño que consiste en especificar comportamiento a componentes.

En esta sección nos vamos a centrar en la Inyección de Dependencias por Constructor. El diagrama de diseño es el siguiente:

alt text

Tenemos una clase ClassA que tiene una dependencia con el componente IServiceA. Builder se encarga de crear el objeto ClassA y de inyectarle la dependencia IServiceA creando una instancia de su implementación ServiceA.

Ejemplo

alt text

ClassA no tiene que preocuparse de conocer cómo se instancia la dependencia IServiceA ya que el componte que se encarga de crear ClassA ya se preocupa de inyectarle una implementación correcta de IServiceA.

Existen muchos frameworks que se encargan de registrar y proveer las dependencias. A continuación vamos a ver un ejemplo muy sencillo de uno de estos frameworks llamado AutoFac.

alt text

Como podemos ver en el código, creamos un contenedor (ContainerBuilder). Mediante las funciones RegisterType definimos las dependencias de la siguiente manera:

RegisterType().As().

Realizando esto le estamos ordenando que cada vez que se requiera una implementación de un servicio nos devuelva la implementación que nosotros le hemos especificado.

En próximos artículos explicaremos con más detalles los diferentes frameworks para inyectar dependencias como por ejemplo AutoFac, Unity…