Blog de Fernando Machado Piriz

Artículos sobre transformación digital, arquitectura empresarial y temas relacionados

Posts Tagged ‘MEF

¿Das presentaciones frecuentemente? Mira esto

with 3 comments

Para usuarios finales: Giving a Presentation es una aplicación simple para cambiar ciertas configuraciones del equipo mientras damos una presentación, por ejemplo, ocultar los íconos del escritorio, inhabilitar el protector de pantalla, cerrar ciertos programas, etc. Los cambios son deshechos cuando la presentación termina. Inicialment construida como un ejemplo para mostrar ciertas tecnologías, la aplicación se volvió útil por sí misma.

Miren este video para ver cómo funciona Giving a Presentation:

Si quieren probar Giving a Presentation por ustedes mismos, pueden descargar los archivos de instalación desde Codeplex.

Para desarrolladores: Giving a Presentation es una aplicación WPF de ejemplo creada en C# que uso como ejemplo en algunos artículos sobre Managed Extensibility Framework, Dependency Injection and Inversion of Control, etc. que escribo en este blog. Demuestra cómo construir una aplicación extensible formada por un contenedor de partes que lo componen.

En esta aplicación de ejemplo, el contenedor es la ventana printicpal, más el tab control en ella. El tab control tiene solo una tab Default y otra About. La tab Default está inicialmente vacía; va a ser completada dinámicamente con las partes componentes.

La tab About muestra la información típica sobre la versión de la aplicación, etc.

Las partes componentes son clases que necesitan cumplir tres requisitos:

  1. Deben implementar una interfaz GivingAPresentation.Common.Contract.
  2. Deben estar decoradas con el atributo Export de System.ComponentModel.Composition.
  3. Deben ser distribuidas en un ensamblado en la misma carpeta que el contenedor.

El Managed Extensibility Framework y la clase MainWindow de la aplicación hacen el resto. El primero carga en una lista instancias de todos los tipos exportados que implementan la interfaz requerida mencionada. El segundo toma las instancias de la lista y agrega los controles en el tab apropiado del tab control.

Hasta ahora he creado cuatro partes componentes para resolver algunos de los problemas típicos que aparecen cuando damos una presentación:

  • Ocultar los íconos del escritorio. Habitualmente ponemos documentos en el escritorio. Algunos de esos documentos son privados y no queremos que la audiencia los vea antes que proyectemos nuestra presentación. Usando esta característica es posible ocultar los íconos del escritorio durante una presentación y mostrarlos nuevamente al final.
  • Inhabilitar el protector de pantalla. Podemos tener el protector de pantalla configurado para iniciar luego de cierto tiempo de inactividad. Si pasamos mucho tiempo en la misma diapositiva durante una presentación, el protector de pantalla puede interrumpir la presentación. Usando esta característica es posible inhabilitar el protector de pantalla durante la presentación y habilitarlo nuevamente al final.
  • Cerrar programas. Algunos programas muestran ventanas de notificación, por ejemplo, los clientes de correo electrónico o mensajería instantánea. Más allá de la interrupción, el contenido de la notificación puede ser inapropiado para que la audiencia lo vea durante nuestra presentación. Usando esta característica es posible cerrar programas que se seleccionen, e iniciarlos nuevamente al finalizar la presentación.
  • Cambiar u borrar el fondo de pantalla. ¿No sería genial poder cambiar el fondo de pantalla con el logo de nuestra empresa o con el logo del evento en el que estamos presentando para que la audiencia lo vea mientras no estamos proyectando la presentación? Usando esta característica es posible reemplazar la imagen del fondo de pantalla por una seleccionada o remover la imagen del fondo durante una presentación, volviendo todo a como esta inicialmente al finalizar.

Miren este video para ver cómo estas partes efectivamente extienden el contenedor dinámicamente:

También pueden descargar el código fuente de Giving a Presentation desde Codeplex.

Pruébenla. ¿Les gusta? ¿Es espantosa? Déjenme saber.

Anuncios

Simple Introducción al Managed Extensibility Framework

with 3 comments

Es muy común que los desarrolladores ocupemos más tiempo modificando aplicaciones existentes que construyendo aplicaciones nuevas desde cero. Nuevos requerimientos de negocio suelen precisar de nuevas funcionalidades en las aplicaciones. La forma como se agregan estas nuevas funcionalidades hoy en día termina cuando generamos, probamos y distribuimos nuevamente la aplicación. Estas actividades generalmente afectan la aplicación completa y no sólo la funcionalidad agregada.

Cuando los requerimientos cambian, los desarrolladores debemos cambiar las funcionalidades de la aplicación. Después de años de evolución, el código que implementa estas funcionalidades suelen tener dependencias indeseables y esas modificaciones pueden tonar la aplicación inestable, o requerir extensas pruebas de regresión que aseguren que no se han introducido errores. También en este caso la historia termina en generar y distribuir de nuevo toda la aplicación.

Cada vez más los negocios cambian más y más rápido, para sobrevivir en un mundo global y competitivo. Y el software que hace que esos negocios funcionen, también tiene que poder cambiar y tiene que poder cambiar rápido.

Supongamos por un instante que cada funcionalidad de la aplicación puede ser descompuesta en una “parte”. Lo que necesitamos es la capacidad de desarrollar partes débilmente acopladas, independientes no sólo en su estructura sino también en su prueba y en su distribución; y que esas partes pueden ser fácilmente compuestas en una aplicación.

Los principios de Ingeniería de software para resolver esta situación se conocen desde hace tiempo y muchas organizaciones los aplican con éxito. Pero las soluciones suelen ser caso a caso y no están disponibles al público en general como nosotros.

Recientemente han aparecido frameworks para desarrollo de aplicaciones en partes. MEF es uno de ellos.

En MEF existen los siguientes roles:

  • Partes exportadas. Declaran que pueden ser usadas para componer una aplicación y el contrato que implementan. Son unidades independientes de desarrollo, compilación y distribución. Estar débilmente acopladas, tanto como las demás partes como la aplicación que compondrán, es decir, una parte no necesariamente conoce a las demás, ni sabe necesariamente en qué aplicación será usada.
  • Puntos de importación. Variables que contienen partes o colecciones de partes importadas que deben implementar cierto contrato. Las partes son creadas automáticamente a partir de la información contenido en catálogos de partes.
  • Catálogos de partes. Contienen definiciones de partes: dónde están y qué contrato implementan.
  • Contenedores de partes. Contienen partes instanciadas y realizan la composición de las partes.

A continuación voy a mostrarles cómo desarrollar una aplicación con MEF paso a paso. Para entender MEF la aplicación debe ser simple, pero debe ofrecer varias funcionalidades que se puedan descomponer en partes.

La aplicación en este caso me permite escribir texto y luego puedo transformarlo aplicando diferentes algoritmos. Cada algoritmo ofrece una funcionalidad diferente y eso es lo que voy a transformar en partes. La aplicación luce así:

image

La lista desplegable muestra las transformaciones que puedo aplicar:

image

El objetivo es desarrollar la interfaz de usuario y cada transformación de forma independiente. Ahí vamos.

El contrato que deben implementar las partes lo declaro con una interfaz IFilter:

public interface IFilter
{
    string Filter(string input);
}

Luego creo una parte (la transformación ToUnicodeFilter) implementando esa interfaz IFilter y declarándola como exportable en MEF con el atributo Export:

[Export(typeof(IFilter))]
public class ToUnicodeFilter : IFilter
{
    public string Filter(string input)
    {
        StringBuilder output = new StringBuilder();
        foreach (char item in input)
        {
            output.AppendFormat(" U+{0:x4}", (int)item);
        }
        return output.ToString();
    }
}

La transformación en este caso es convertir cada letra del texto recibido en su correspondiente representación Unicode.

Puedo tener tantas partes como quiera, mientras también implementen la interfaz IFilter y estén decoradas con el atributo Export.

Ahora vamos a la aplicación a programar la composición de las partes en la aplicación. Necesito un catálogo que describa las partes. En este ejemplo voy a tener todos los ensamblados con las partes en una carpeta llamada Extensions, así que uso un catálogo DirectoryCatalog.

DirectoryCatalog catalog = new DirectoryCatalog("Extensions");

También necesito un contenedor para las instancias de las partes y para armar la composición. Al contenedor le paso el catálogo para que tenga la información de dónde encontrar las partes:

CompositionContainer container = new CompositionContainer(catalog);

Ya casi estamos. Lo que necesito ahora es un punto donde importar las partes. En este caso puede haber varias transformaciones, así que declaro una IList<IFilter> que decoro con el atributo Import:

[ImportMany(typeof(IFilter))]
private IList<IFilter> filters = new List<IFilter>();

Lo último que hago es componer las partes, indicándole al contenedor dónde están los puntos de importación:

container.ComposeParts(this);

Luego puedo recorrer la lista para poblar los filtros en la lista desplegable:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    foreach (IFilter filter in filters)
    {
        comboBoxFilters.Items.Add(filter.GetType());
    }
}

Cuando el usuario hace clic en el botón Apply, se busca el filtro a ejecutar en la lista de filtros a partir del elemento seleccionado en la lista desplegable:

private void buttonApply_Click(object sender, RoutedEventArgs e)
{
    if (comboBoxFilters.SelectedIndex != -1)
    {
        int index = comboBoxFilters.SelectedIndex;
        IFilter filter = filters.ElementAt(index);
        textBoxOutput.Text = filter.Filter(textBoxInput.Text);
    }
}

Tres ensamblados intervienen aquí. El primero es donde está declarado IFilter. El segundo es donde está implementada la clase ToUnicodeFilter. El tercero es el de la propia aplicación. Lo interesante aquí es que para agregar una nueva transformación o para modificar una existente, lo único que tengo que hacer es distribuir el ensamblado que la contiene en la carpeta Extensions. El resto de los ensamblados no se ven afectados, ni siquiera el de la aplicación.

¿Cómo se logra que esto funcione casi de forma máquina? Cuando invoco el método ComposeParts del contenedor pasando como parámetro la propia aplicación, el contenedor busca todas las variables decoradas con ImportMany (o con Import) en la aplicación. En cada caso aparece también la interfaz requerida por cada punto de importación.

El contenedor también conoce el catálogo, pues lo paso como parámetro en el constructor. Como el catálogo tiene la información de todas partes, incluyendo qué partes implementan qué interfaz, puede encontrar que parte o partes implementan la interfaz de cada punto de importación. Como el catálogo también conoce en qué ensamblados están las partes, puede crear instancias de las partes apropiadas y asignarlas a las variables correspondientes a los puntos de importación.

Al final, la composición “se arma sola”, el trabajo lo hace MEF y no el programador. El programador solo “decora” los tipos con Export, las variables con Import, crea uno o más catálogos y el contenedor, y finalmente compone las partes. Simple, ¿no?

Recuerden que el desafío era desarrollar partes débilmente acopladas, independientes no sólo en su estructura sino también en su distribución; y que esas partes pudieran ser fácilmente compuestas en una aplicación. ¿Objetivo cumplido? Yo creo que sí.

Pueden descargar el código de esta aplicación de aquí. El ejemplo contiene más partes todavía de las que estoy mostrando aquí.

Vean también MEF Home y MEF Overview (en inglés).

En un próximo post voy a hablar de cómo asociar metadata a las partes, por ejemplo, para que en lugar de poner el nombre del tipo en la lista desplegable, aparezca un nombre más adecuado. Nos vemos.

Written by fernandomachadopiriz

15/04/2010 at 10:46