viernes, julio 31, 2009

Plugin Pattern II

En la practica cotidiana existen muchas oportunidades en la que se desea extender la funcionalidad de un sistema, tal sea para implementar de otra forma un funcionalidad existente como agregar una que no exista.

Una alternativa de solución

Usando herencia se puede implementar de otra forma una funcionalidad existente respecto a una clase original.

Por ejemplo en un sistema simple de validación de datos de entrada en donde tenemos que validar los campos tipo string, valores requeridos y uno alfanumérico con un formato predefinido, se podría implementar de la siguiente forma




La clase ValidateEngine permite validar todos los datos de entrada, obviamente esta implementación está muy simplificada pues se debería agrega toda la lógica de validación de datos de entrada de un determinado formulario del front end de la aplicación.

Esto trae como problema que se deba implementar lógica para validar tipo de datos, longitud, expresiones regulares (por ej para un ISBN) y valores requeridos, es decir una implementación con una muy baja cohesión.

Una mejora sería que la clase ValidateEngine sea una clase abstracta del cual se deba heredar para implementar cada tipo de validación.


De esta forma tenemos diferentes validaciones implementadas en clases separadas, lo que permite que cada una de ellas tenga alta cohesión.

Para utilizar este modelo podríamos aplicar el patrón estrategia [3] tal como muestra la figura.


Aplicando este patrón tendríamos un sistema más flexible, por ejemplo en C# se podría implementar de la siguiente manera:


http://codepaste.net/ocb7gy

Como resultado de la ejecución de este ejemplo obtenemos la siguiente respuesta



Si deseamos agregar una nueva forma de implementar una validación deberíamos heredar otra clase y configurarla en la clase de contexto.

Esta solución es mucho mejor que utilizar una sola clase para todo pero tiene el gran inconveniente que para agregar nuevas funcionalidades se debe crear una clase heredada lo que hace el sistema poco flexible.

¿Cómo mejoramos está solución?

Supongamos que deseamos crear un sistema en donde se pueda expandir su funcionalidad pero en forma dinámica, configurable y en tiempo de ejecución, la solución mencionada ya no satisface ese requerimiento.

La implementación del patrón Plugin permite agregar funcionalidad a nuestro sistema en forma dinámica utilizando configuración o un estándar de implementación predefinidos. Los módulos que agregan funcionalidad al sistema se denominan plugins.

Ejemplos de la vida real

Este patrón es muy utilizado en múltiples sistemas como sistemas multimedia que permiten agregar en forma muy simple nuevos módulos para edición de audio, transformación entre formatos, skins, analizadores de espectro etc.

En el mundo del desarrollo este patrón se aplica mucho, el más claro ejemplo es el IDE Eclipse,

la arquitectura de este entorno de desarrollo está exclusivamente basada en plugins, pues por si solo no tiene funcionalidad significativa más que proveer un entorno que permite agregarlos [4].

En sus orígenes fue creado como un entorno de desarrollo para Java, pero actualmente se puede usar para muchos otros lenguajes (C++, Python, PHP, Perl, Intel C++ para Linux, Cobol, ABAP , Google apis etc.), también se puede usar para modelado (UML, Base de datos, Data Warehouse etc.), herramientas de soporte al desarrollo como gran cantidad de editores diferentes, clientes Subversion, para soporte de programación en pares distribuida (Distributed XP) , comerciales como herramientas para el BPM de Tibco, generación de interfases para el broker de Software AG etc , al día de la fecha existen más de 1200 plugins publicados.

Visual Studio .Net tiene una api que permite crear plugins (addons), esta api esta implementada en System.Addin. La versión para desarrolladores del Visual Studio Team System posee plugins para revisión de código (basado en el FXCop) para pruebas unitarias, para análisis de performance, cobertura de código, control de fuentes, implementación etc.

Se pueden utilizar plugins comerciales o bien integrarlos al Visual Studio mediante un desarrollo especial.


Veamos una aplicación práctica

En el siguiente ejemplo vamos a extender el sistema de validación para aplicar el patrón Plugin, en este caso se va a utilizar configuración xml y reflection para poder agregar en forma dinámica nuevos mecanismos de validación.

Las nuevas validación estarán implementadas en assemblies separados y usarán el patrón Separate Interfase [5] para implementar la interfase IValidate que está en el assembly Context.

Cada implementación de una validación diferente se implementa en assemblies separados y mediante configuración se agregan como plugin al sistema original.


En este ejemplo usamos el siguiente archivo de configuración.


<ValidatePluginsConfigFile>

<plugins>

<plugin>

<name>ValidateRegularExpresions name>

<fullName>ValidationExample.ValidateRegularExpresions,Version=1.0.0.0,Culture=neutral,

PublicKeyToken=3d10efd7d0a5d829fullName>

<type>ValidationExample.ValidateRegularExpresions.ValidateRegularExpresionstype>

<active>trueactive>

plugin>

<plugin>

<name>ValidateStringsname>

………………………...

plugin>

………………………...

plugins>

ValidatePluginsConfigFile>


Por lo tanto podemos agregar nuevos nodos plugin por cada componente de validación y el programa automáticamente los invocará.

Un poco más de código

En el siguiente fragmento de código se muestra como se puede ejecutar en forma dinámica un método de un assembly externo usando reflection y un poco de lógica para leer la configuración


http://codepaste.net/k9bih8

Considerando que para que funcione correctamente los plugins deben estar en la GAC el resultado de la ejecución de este programa es idéntico al caso anterior.


Código

El código de ejemplo mostrado es parcial , de este link pueden bajar el proyecto completo para probar en sus maquinas, fue creado con el Visual Studio 2005 SP1.

Conclusión

El patrón plugin es muy útil para extender la funcionalidad de un sistema de forma tal que podemos construir una sistema base que permita agregar plugins y luego esos plugins pueden ser construidos por terceros, esta filosofía la usa el IDE Eclipse ya mencionado y también es muy utilizada en el navegador Firefox (Firefox extensions) y el Chrome de Google.

También se usa en sistemas comerciales cuyos plugins requieren el pago de licencias adicionales al sistema principal.

Como desventaja se debe considerar el aumento del complejidad para permitir el agrego de los plugins en forma simple por un usuario que no tiene porque conocer los detalles del sistema principal.

Como en muchos casos para hacer un sistema que permita utilizar este patrón se debe considerar en el diseño original de la arquitectura del mismo.


Bibliografía


[1] Pattern to Enterprise Applications Architecture - Plugin Pattern ,http://martinfowler.com/eaaCatalog/plugin.html


[2] Log4Net Framework , http://logging.apache.org/log4net/index.html


[3] Erich Gamma et at, “Design Patterns: Elements of Reusable Object-Oriented Software” (Addison-Wesley Professional Computing Series) , pag


[4] Eclipse plugin central web site, http://www.eclipseplugincentral.com/


[5] Erich Gamma et at, “Design Patterns: Elements of Reusable Object-Oriented Software” (Addison-Wesley Professional Computing Series) , pag


[6] Pattern to Enterprise Applications Architecture - Separate Interface pattern, http://martinfowler.com/eaaCatalog/separatedInterface.html



2 comentarios:

Felixls dijo...

Muy bueno el artículo, podrías subir algún ejemplo de código a 4shared (por ej.).

gracias pata!

Sergio Salanitri dijo...

Gracias Felix, subi el proyecto completo al repositorio online

http://www.4shared.com/get/123115125/322f5fae/ValidationEngine2.html

Saludos