En días pasados me encontré con un problema en el cual se podría utilizar IoC para resolverlo de forma sencilla y elegante. En este post voy a reproducir este fix con un problema un tanto diferente pero en el cual se puede ver como de forma sencilla podemos aprovecharnos de las bondades de Unity y a la vez hacer nuestra aplicación más versátil y fácil de hacer crecer.
El Problema
Supongamos que tenemos un sistema que hace una seria de funciones para la empresa y que luego de una fusión con otra empresa, tenemos un sistema similar pero que además realiza unas funciones extras. Resulta que cada sistema tiene su propia tabla de usuarios ( así es, como siempre primero se hacen los sistemas y luego se piensa en la seguridad y no a la inversa como debería ser ) y se quiere hacer una versión de forma rápida que mezcle las funcionalidades de ambas aplicaciones. La aplicación va a tener un ComboBox en donde el usuario indica a cual empresa originalmente pertenece y el sistema debe resolver a donde debe de ir a autenticarse el usuario. En primera instancia, la primera aplicación es desktop y la segunda aplicación es Web. El problema radica en cómo manejar estos tipos diversos de usuario en una sola aplicación sin necesidad de andar previendo con cual tipo estamos trabajando.
La Solución
La solución a este problema esta por el lado del polimorfismo, ya que si definimos una interface IUsuario, podemos trabajar con la interface y no con el tipo concreto, lo cual nos ahorraría el peso de estar manejando un tipo de usuario X en la aplicación cuando se requiera. El siguiente diagrama nos muestra lo expuesto anteriormente.
El código de cada una de las clases es el que se presenta a continuación:
namespace Core.LogicaDeNegocios
{
//Este usuario se autentica en una base de datos para usuarios Web
public class Usuario_Web: IUsuario
{
public IUsuario Autenticar()
{
Trace.WriteLine("Autenticar Usuario Web");
return this;
}
public void Autorizar(object _form)
{
Trace.WriteLine("Autorizar Usuario Web");
}
}
}
namespace Core.LogicaDeNegocios
{
//Este usuario se autentica utilizando una base de datos de usuarios locales
public class Usuario : IUsuario
{
public IUsuario Autenticar()
{
Trace.WriteLine("Autenticar Usuario");
return this;
}
public void Autorizar(object _form)
{
Trace.WriteLine("Autenticar Usuario");
}
}
}
Ahora el punto es como hacer que la aplicación construya la instancia que corresponda – dependiendo de la selección del comboBox – sin necesidad de estar creando clases concretas por toda la aplicación. Para este caso es que se decidió utilizar Unity. Con Unity tenemos la posibilidad de registrar los tipos que deseamos y mapearlos con clases concretas para cuando solicitemos el tipo, se nos retorne la instancia que deseamos. Para registrar nuestras clases anteriores vamos a utilizar el archivo app.config de nuestra aplicación desktop y vamos a agregar la siguiente configuración.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<alias alias="IUsuario" type="Core.LogicaDeNegocios.IUsuario, Core.LogicaDeNegocios" />
<namespace name="Core.LogicaDeNegocios" />
<assembly name="Core.LogicaDeNegocios" />
<container>
<register type="IUsuario" name="usuario" mapTo="Usuario" />
</container>
<container name="UsuarioWeb">
<register type="IUsuario" name="usuarioWeb" mapTo="Usuario_Web" />
</container>
</unity>
</configuration>
Como podemos ver en el xml anterior, estamamos registrando dos tipos con la interface IUsuario: Usuario y Usuario_Web, para que cuando solicitemos el tipo Unity tenga como resolver y pueda crear la instancia que deseamos. El siguiente paso es utilizar el contenedor de Unity para que nos resuelva el tipo y dado que no queremos que el UI se tenga que preocupar por este block, vamos a crear una clase estática que nos ayude retornando la instancia a partir de un tipo especificado como parámetro en una función que vamos a crear para crear la instancia de acuerdo al tipo que solicita el usuario.
//Unity
using Microsoft.Practices.Unity;
namespace Core.LogicaDeNegocios
{
public static class AdministradorUsuario
{
public static IUsuario ObtenerInstancia<T>() where T:IUsuario
{
using (IUnityContainer container = new UnityContainer())
{
return container.Resolve<T>();
}
}
}
}
Nótese el constraint en el Generic, el cual nos delimita los tipos de datos que pueden ser T, en este caso T tiene que implementar IUsuario. Por último procedemos a crear el código en la forma de log in, en donde de acuerdo a la opción seleccionada por el usuario vamos a seleccionar el tipo que queremos pasar como T a la función ObtenerInstancia<T>.
IUsuario _usuarioActual;
public IUsuario UsuarioActual
{
get { return _usuarioActual; }
set { _usuarioActual = value; }
}
private void btnLogin_Click(object sender, EventArgs e)
{
if (cmbEmpresa.SelectedItem == "Empresa A")
UsuarioActual = AdministradorUsuario.ObtenerInstancia<Usuario>().Autenticar();
else
UsuarioActual = AdministradorUsuario.ObtenerInstancia<Usuario_Web>().Autenticar();
}
En este caso específico, estamos pasando el tipo concreto al método que nos devuelve una instancia de la clase solicitada, y como ambas clases deben de implementar la interface IUsuario podemos invocar el método Autenticar que es parte de este contrato. Este método nos devuelve una instancia de IUsuario el cual podemos utilizar en nuestra aplicación para operaciones posteriores de Autorización. Para cerrar el post vemos la estructura de proyecto utilizada, la cual como veremos en el siguiente post nos va a permitir cargar clases de lógica de negocios dependiendo de la forma en que se configure el sistema.
No hay comentarios:
Publicar un comentario