sábado, 29 de mayo de 2010

Mapeo de Delegados

El Problema

Bien, hace unos días trabaja en la implementación de una clase en la cual uno de sus miembros recibe una clase base abstracta. Al momento de instanciar un objeto de este tipo, el constructor "customizado" debía decidir de acuerdo a un tipo específico, de un Enum, que recibe como parámetro, cuál era la clase concreta adecuada para dicha propiedad.

Ante el hecho, de que por las peculiaridades de la clase, el volverla genérica no implicaba beneficio alguno, la prima opción sería implementar un switch, lo cual era simplificado por la existencia del Enum.

Hablando un poco con un compañero de trabajo (Pablo Guerrero, mejor conocido como Chino), me comentó que había leído sobre el mapeo de delegados, el cual se convierte en una elegante opción al switch.

El Ejemplo

Suponiendo que tenemos una biblioteca la cual tiene libros digitales, de audio y de papel. Existe una clase base abstracta Libro y clases concretas para cada tipo de libro.

El problema es que al ingresar un libro, recibimos información indiscriminada, con el único detalle que se nos indican el tipo de libro que es.

Las Clases

   1:      public enum TipoLibro
   2:      {
   3:          Digital,
   4:          Audio,
   5:          Papel
   6:      }
   7:   
   8:      public abstract class Libro
   9:      {
  10:          public int IdLibro { get; set; }
  11:          public string Nombre { get; set; }
  12:          public string Autor { get; set; }
  13:          public int NumeroCapitulos { get; set; }
  14:          public DateTime FechaPublicacion { get; set; }
  15:   
  16:      }
  17:   
  18:      public class LibroDigital : Libro
  19:      {
  20:          public string Formato { get; set; }
  21:      }
  22:   
  23:      public class LibroPapel : Libro
  24:      {
  25:          public string TipoPapel { get; set; }
  26:      }
  27:   
  28:      public class LibroAudio : Libro
  29:      {
  30:          public int NumeroVoces { get; set; }
  31:      }

La magia será crear un mapa de delgados, para ello nos valemos del delegado Func para no tener que declarar ningún tipo de delegados personalizado, simplemente utilizando métodos que correspondan a la firma del método definida por el delegado. Este delegado en particular recibe un un string y retorna un tipo Libro.

   1:          private Dictionary<TipoLibro, Func<string, Libro>>
   2:              _mapaTipoLibro = new Dictionary<TipoLibro, Func<string, Libro>>()
   3:          {
   4:             { TipoLibro.Digital, CrearLibroDigital },
   5:             { TipoLibro.Audio, CrearLibroAudio },
   6:             { TipoLibro.Papel, CrearLibroPapel }
   7:            
   8:          };

Nótese que lo que se hace es crear un diccionario en donde la llave es un Enum y el delegado es el valor. Para accederlo simplemente llamamos el diccionario.

   1:          List<Libro> Libros = new List<Libro>();
   2:  

   3:          public void AgregarLibro(string nombre, TipoLibro tipoLibro)
   4:          {
   5:              Libros.Add(_mapaTipoLibro[tipoLibro](nombre));
   6:          }


Este método además de elegante simplifica el código y nos evita algunos switch.

Referencias

Aquí pueden encontrar un poco más sobre los delegados Func y el blog original Making The Complex Simple donde el buen chino encontró la info.

No hay comentarios: