Disponible Microsoft Enterprise Library 5.0

Ya está disponible la versión 5.0 de las Enterprise Librarys. Descarga aquí.

Anuncios

Inversión de Control e Inyección de dependencia

En esta entrada voy a comentarles el uso de dos patrones que básicamente lo que intentan es evitar que una clase que sufre dependencias de otras, no sea la responsable de crearse a sí misma o no de recibir otras clases cómo parámetros.

Para poder entender estos patrones vamos a imaginar que estamos trabajando en un proyecto, cuyo objetivo es leer contenido desde una red social: por ejemplo desde Twitter o Facebook. Lo primero que podríamos hacer es crear una clase por cada red social que necesitemos consultar.

Para ello vamos a crear dos clases: FacebookServices y TwitterServices y en cada una de ellas vamos a agregar un método que recupera la lista de contactos del usuario:

List<Contactos> ObtenerContactos(string usuario, string password)

Además vamos a tener una tercer clase que se va a encargar de centralizar la lógica del servicio: RedSocialService. Esta clase va a tener como parámetro del constructor el servicio de la red social que voy a utilizar:

 public class RedSocialService
 {
     public FacebookServices  Service { get; set; }

     public RedSocialService(FacebookServices service)
     {
         this.Service = service;
     }
 }

Uso directo de las clases (nivel de máximo acoplamiento)

Primer problema de dependencia que encontramos: qué servicio voy a utilizar para recuperar los usuarios?, podría obtenerlos de cualquiera de las redes sociales que tengamos definidas como servicio. Por ejemplo podríamos hacer:

FacebookServices servicioFacebook = new FacebookServices();
RedSocialService servicio = new RedSocialService (servicioFacebook);

var contactos = servicio.ObtenerContactos(usuario, password);

En este caso recuperaríamos los contactos desde que tiene en Facebook. Ahora supongamos que queremos entregar esta aplicación a un cliente que no posee una cuenta en este red social y desea obtener sus contactos desde Twitter por ejemplo, entonces deberíamos modificar todas las instancias a este servicio por el servicio de esta otra ubicación, además de modificar el constructor de la clase RedSocialService. Finalmente generar una nueva aplicación por cada tipo de red social que nos soliciten y tener de esta forma múltiples ejecutables.

Esto no es nada divertido y óptimo. podemos ver ahora la gran dependencia que hay en nuestro código y los problemas que está representa.

Uso de interfaces (nivel de acoplamiento medio)

Para evitar esta fuerte dependencia, podríamos hacer uso de las interfaces. Para esto vamos a crear la interfaz IRedSocialService y vamos a agregarle la firma:

 List<Contactos> ObtenerContactos(string usuario, string password);

Y hacer que tanto FacebookServices como TwitterServices implementen de ella.

Luego modificamos el constructor de RedSocialService para que reciba como parámetro un servicio que implemente IRedSocialService.

 public class RedSocialService
 {
     public IRedSocialService Service { get; set; }

     public RedSocialService(IRedSocialService service)
     {
         this.Service = service;
     }
 }

Finalmente nuestra llamada:

IRedSocialService servicio = new FacebookServices();

RedSocialService miServicio = new RedSocialService(servicio);
var contactos = servicio.ObtenerContactos(usuario, password);

Como podemos ver el nivel de dependencia disminuyo considerablemente. Ahora ante un cambio, solo debemos modificar nuestras instancias de IRedSocialService . Si bien mejoramos nuestro código, aún sigue existiendo dependencia.

Patrón Factory (nivel de acoplamiento bajo)

Este patrón lo que permite es delegar a otra clase la creación del servicio apropiado. Para esto vamos a crear una nueva clase a la que vamos a llamar RedSocialFactory. En ella vamos a crear un método que vamos a llamar ObtenerServicio , el cual tendrá como parámetro el nombre de la red social que usaremos y nos devolverá el servicio correspondiente y que por supuesto implementará IRedSocialService:

public static IRedSocialService ObtenerServicio(string nombreRedSocial)
{
    switch (nombreRedSocial)
    {
        case "Facebook":
             return new FacebookServices();
        case "Twitter":
              return new TwitterServices();
        default:
              throw new ArgumentOutOfRangeException("Red social no soportada.");
     }
}

Por lo tanto ahora podríamos definir dentro de un archivo de configuración o un archivo de recursos que red social vamos a utilizar en la aplicación, y hacer nuestra llamada de la siguiente forma:

IRedSocialService servicio = RedSocialFactory.ObtenerServicio(ConfigurationManager.AppSettings["RedSocial"]);

RedSocialService miServicio = new RedSocialService(servicio);
var contactos = miServicio.ObtenerContactos(usuario, password);

En este caso nos evitaremos entre otras cosas, generar un ejecutable por cada servicio, y al momento de entregar al cliente solo modificar el parámetro que corresponde a la red social. Además no deberemos modificar en nada el resto del código.

Inyección de dependencias (nivel de acomplamiento mínimo)

Basicamente IoC trabaja con el mismo principio de la Factory, solo que el mismo nos proporciona una forma de hacer esta tarea más sencilla y prolija, además de permitirnos realizar otras configuraciones.

Si bien existen muchísimos frameworks de IoC, como por ejemplo Castle, Spring o Ninject, nosotros vamos a utilizar Unity que nos proporciona Patterns & Practices.

Patterns & Practices

Patterns & Practices

Este framework nos brinda un contenedor, en el cual vamos a mapear nuestras clases a instancias, por lo tanto cuando solicitemos una instancia de cierto tipo, nos devolverá la clase que hemos definimos. Para esto en nuestro proyecto primero deberemos referenciar a:

using Microsoft.Practices.Unity;

Luego hacemos el mapeo entre la interfaz y clase:

IUnityContainer container = new UnityContainer();
container.RegisterType<IRedSocialService, FacebookServices>();

Finalmente pedimos al contenedor que se encargue de resolver y recuperar la clase para la instancia solicitada:

IRedSocialService servicio = container.Resolve<IRedSocialService>();

RedSocialService miServicio = new RedSocialService(servicio);
var contactos = miServicio.ObtenerContactos(usuario, password);

Pero hay mucho más y mejor :), supongamos que defino de esta forma al servicio:

RedSocialService servicio = container.Resolve<RedSocialService>();

Unity buscará si existe un mapeo para la clase RedSocialService, como no lo encuentra ya que no es una interfaz, buscará en los parámetros de su constructor, al ver que es del tipo IRedSocialService, entonces buscará si existe el mapeo de la misma, y si existe instanciara la clase al parámetro, es decir, inyectará los parámetros del constructor.

Espero que les sea de utilidad!