12.30.2010

IComparer<T>, IComparable<T> y el SortedList<TKey, TValue>

Revisando la implementación del SortedList y como alterar los criterios de comparación me di a la tarea de buscar alternativas para resolver un problema que tenía en un software en el que estaba trabajando.

Resulta que cuando uno utiliza un SortedList tiene dos opciones para manejar el ordenamiento de la misma: IComparable e IComparer. ¿Y cuál es la diferencia? Bueno desde el punto de vista desarrollo de aplicaciones 2:

  1. Tengo acceso al código de la clase que quiero ordenar.
  2. La clase esta en un componente a y no tengo acceso a su código.

IComparable<T>

En primera instancia, supongamos que tenemos acceso al código de la clase que queremos tener dentro de nuestro SortedList. En este caso utilizamos la interface IComparable<T> ya que el SortedList utiliza el método CompareTo de la implementación de IComparable<T> para realizar la ordenación; esta interface además es la que le da el servicio para ordenar al método Sort del Array. Para poder utilizar esta interface debemos implementarla en la clase que vamos a utilizar tal y como lo muestra el siguiente código.

public class Estudiante : IComparable<Estudiante>
{
public int Id { get; set; }
public string Nombre { get; set; }
public string Apellidos { get; set; }

public Estudiante( int pId, string pNombre, string pApellidos )
{
Id = pId;
Nombre = pNombre;
Apellidos = pApellidos;
}

public int CompareTo( Estudiante other )
{
return this.Apellidos.CompareTo( other.Apellidos );
}

}

Con esta implementación estamos haciendo que cuando se use la clase Estudiante en colecciones genéricas se ordene por los apellidos del estudiante. Si vemos la siguiente lista y la recorremos, veremos que los ítems fueron ordenados por medio de los apellidos.
SortedList<Estudiante, Estudiante> _listaEstudiantes = new SortedList<Estudiante, Estudiante>();

_listaEstudiantes.Add( _estudiante1, _estudiante1 );
_listaEstudiantes.Add( _estudiante2, _estudiante2 );
_listaEstudiantes.Add( _estudiante3, _estudiante3 );
_listaEstudiantes.Add( _estudiante4, _estudiante4 );
_listaEstudiantes.Add( _estudiante5, _estudiante5 );
_listaEstudiantes.Add( _estudiante6, _estudiante6 );
_listaEstudiantes.Add( _estudiante7, _estudiante7 );

foreach (KeyValuePair<Estudiante, Estudiante> item in _listaEstudiantes)
{
Console.WriteLine( "Apellidos: {0} | Nombre: {1}", item.Value.Apellidos, item.Value.Nombre );
}
Al ejecutar este código el resultado será el siguiente:
image

IComparer<T>


Entonces cual es el uso que le vamos a dar a IComparer<T>. Bueno, en realidad lo que nos da es un mecanismo extra de comparación para ordenar la lista. Este mecanismo es útil cuando por ejemplo queremos utilizar una lista ordenada para la clase pero no tenemos el código de la clase y por lo tanto no podemos implementar la interface IComparable<T>. Supongamos la siguiente clase Estudiante:
public class Estudiante 
{
public int Id { get; set; }
public string Nombre { get; set; }
public string Apellidos { get; set; }

public Estudiante( int pId, string pNombre, string pApellidos )
{
Id = pId;
Nombre = pNombre;
Apellidos = pApellidos;
}

}

Supongamos que viene en un dll que no podemos modificar y sin embago la queremos ordenar por el criterio que nosotros deseamos. En este caso podemos utilizar la interface IComparer<T>. Lo que tenemos que hacer es crear una clase que implemente la interface IComparer<T> y establecer ahi la forma en que queremos hacer la comparación, en este caso por apellidos.

public class ComparadorEstudiante : IComparer<Estudiante>
{
public int Compare( Estudiante x, Estudiante y )
{
return x.Apellidos.CompareTo(y.Apellidos);
}
}
Seguidamente procedemos a crear la instancia de la clase SortedList<T> indicándole que utilice este comparador en el constructor.

SortedList<Estudiante, Estudiante> _estudiantes = new SortedList<Estudiante, Estudiante>( new ComparadorEstudiante() );

Estudiante _estudiante1 = new Estudiante( 1, "Ana", "Hernandez" );
Estudiante _estudiante2 = new Estudiante( 2, "Luis", "Ruiz" );
Estudiante _estudiante3 = new Estudiante( 3, "Esther", "Benavidez" );
Estudiante _estudiante4 = new Estudiante( 4, "Ulises", "Alonso" );
Estudiante _estudiante5 = new Estudiante( 5, "Ana", "Lopez" );
Estudiante _estudiante6 = new Estudiante( 6, "Mario", "Estrada" );
Estudiante _estudiante7 = new Estudiante( 7, "Clara", "Rodriguez" );

_estudiantes.Add( _estudiante1, _estudiante1 );
_estudiantes.Add( _estudiante2, _estudiante2 );
_estudiantes.Add( _estudiante3, _estudiante3 );
_estudiantes.Add( _estudiante4, _estudiante4 );
_estudiantes.Add( _estudiante5, _estudiante5 );
_estudiantes.Add( _estudiante6, _estudiante6 );
_estudiantes.Add( _estudiante7, _estudiante7 );

foreach (KeyValuePair<Estudiante, Estudiante> item in _estudiantes)
{
Console.WriteLine( "Apellidos: {0} | Nombre: {1}", item.Value.Apellidos, item.Value.Nombre );
}
Ahora la comparación se hará a través de la clase ComparadorEstudiante y el resultado será el siguiente:
image


Algunos podrán decir que para que tanta complicación si se podría pasar en el TKey del KeyValuePair el campo por el que quiero la llave y a partir de ahi que se genere la ordenación por el comparador por defecto del mismo, sin embargo; en el caso donde uno requiere este tipo de ordenación es principalmente cuando la clase que queremos ordenar tiene una relación de 1 a 1 con una clase X y es la que nos va a permtir hacer esta relación; un ejemplo típico es una clase proyecto asociada con una clase ProjectManager y queremos los proyectos ordenados por el project manager.


12.03.2010

Taller de Cloud Computing y Windows Azure

 

Este post no es técnico, más bien es una invitación a un taller que se va a llevar a cabo en el mes de enero acerca de Cloud Computing y Windows Azure y que tendré la dicha de dictar. Espero nos puedan acompañar. Mas información a los teléfonos o direcciones en el brochure o a mi correo diego.rojas [at] formsevolution.net. Hay promos disponibles.

Seminario Cloud Comp

Technorati Tags: ,,

12.02.2010

Utilizando Unity para Resolver un Problema de Negocio - 4

Continuando con la serie de posts acerca de como utilizar Unity y aplicarlo a un problema de negocio, vamos a agregar en este post la funcionalidad necesaria para permitir agregar nuevos tipos de usuario sin tener que compilar nuestra capa de negocios. La idea con este post es permitirle al desarrollador crear componentes que puedan ser asociados a la lógica de negocios sin tener que modificar la misma a través de su código, y que simplemente con agregar el nuevo tipo al archivo de configuración de nuestra aplicación, este pueda utilizarlo. Para ejemplificar mejor el problema voy a mostrar un diagrama de como se vería físicamente nuestro programa. El estado actual de la aplicación a nivel de lógica de negocios es el siguiente:

image

La idea de este post es crear un nuevo usuario que se va a llamar usuario federado, el cual no va a formar parte de nuestro proyecto de Core.LogicaDeNegocios, si no que será un dll aparte el cual implementa una clase del tipo IUsuario llamada Usuario_Federado. Nuestro esquema debería verse de la siguiente manera:

image

Como podemos ver en la figura anterior, ahora vamos a agregar un nuevo dll en donde va a residir nuestro nuevo usuario – en color rojo. Lo interesante con esta solución es que vamos a poder agregar en nuestro BL nuevos usuarios sin tener que ir a recompilar ni modificar nuestra lógica de negocios; esto es lo que vamos a hacer en el post del día de hoy – para que el cambio se pueda manejar de la misma manera en la capa de UI vamos a utilizar Prism en post posteriores.

Solución

En primera instancia vamos a crear un nuevo proyecto del tipo librería y le vamos a poner Core.LogicaDeNegocios.Usuario_Federado. Seguidamente procedemos a crear la clase Usuario_Federado la cual es similar a las demas clases que implementan IUsuario definidas dentro de la librería Core.LogicaDeNegocios.dll. El dll que acabamos de crear requiere una referencia al dll donde esta definido el tipo IUsuario, es decir Core.LogicaDeNegocios.dll. El código de esta nueva clase es el siguiente:

using Core.LogicaDeNegocios;

using System.Diagnostics;

namespace Core.LogicaDeNegocios.Usuario_Federado
{
public class Usuario_Federado : IUsuario
{
IEmpresa _empresa;
public IEmpresa Empresa
{
get
{
return _empresa;
}
set
{
_empresa = value;
}
}

public IUsuario Autenticar()
{
Trace.WriteLine("Autenticar Usuario_Federado");
return this;
}

public void Autorizar(object _form)
{
Trace.WriteLine("Autorizar Usuario Federado");
}
}
}


Ahora vamos a modificar el UI para que se puedan crear instancias de la clase residente en este nuevo dll. Agregamos la “empresa D” la cual estará asociada con el Usuario_Federado y luego procedemos a agregar el siguiente código en el botón con el cual estamos cargando el usuario de acuerdo a los datos ingresados.



string _tipoUsuario = string.Empty;
if (cmbEmpresa.SelectedItem.ToString() == "Empresa A")
_tipoUsuario = "usuario";
else if (cmbEmpresa.SelectedItem.ToString() == "Empresa B")
_tipoUsuario = "usuarioWeb";
else if (cmbEmpresa.SelectedItem.ToString() == "Empresa C")
_tipoUsuario = "otroUsuario";
else
_tipoUsuario = "usuarioFederado";
UsuarioActual = AdministradorUsuario.ObtenerInstancia(_tipoUsuario, txtNombreUsuario.Text, txtPassword.Text).Autenticar();



El paso siguiente es modificar el App.Config para mapear el tipo, es decir IUsuario a Usuario_Federado:



      <register type="IUsuario" name="usuarioFederado" mapTo="Usuario_Federado" />


Por último, ejecutamos la aplicación y podemos ver que si seleccionamos la empresa D, obtenemos una instancia del tipo Usuario_Federado.



image



Como se puede ver en la figura anterior tenemos una instancia de Usuario_Federado, yal y como lo esperabamos. Nótese sin embargo que la empresa esta en nulo pese a que tenemos implementada la propiedad Empresa del tipo IEmpresa ¿Qué nos falta? Pues si vamos a utilizar las empresas ya definidas en nuestro dll de Core.LogicaDeNegocios.dll simplemente tenemos que agregar el mapeo de la propiedad y listo.



 <register type="IUsuario" name="usuarioFederado" mapTo="Usuario_Federado" >
<
property name="Empresa" dependencyName ="empresaMap" dependencyType="IEmpresa"></property>
</
register>



El resultado será el siguiente:



image



Es importante darnos cuenta que hemos agregado una nueva entidad de negocios a nuestra lógica de negocios sin necesidad de recompilar la misma, esto nos dará muchas ventajas cuando queramos crear aplicaciones listas para evolucionar en el futuro. En el próximo post vamos a analizar los beneficios de este tipo de arquitecturas y cuales frameworks y aplicaciones utilizan este tipo de diseño.



Technorati Tags: ,,,,