jueves, 17 de septiembre de 2009

Como independizar tu capa de lógica de tu capa de presentación…

A raiz del siguiente post del excelente blog de Oskar, Julio Trujillo comentó en un comentario (copio literalmente) “Sería interesante una explicación de como convertir Forms a WPF o al menos como poder diseñar una capa que permita conectar la capa de negocio a una de WPF o Forms indistintamente”. A este comentario respondí yo con unas cuantas ideas, pero luego Julio pidió a ver si podiamos exponer las “buenas prácticas” e incluso un ejemplo… Julio, no respondí a tu comentario, simplemente porque el tema es demasiado para un simple comentario, y se merece al menos un post… y estaba sacando tiempo ;)

Creo que el comentario de Julio, encerraba dos preguntas en una: cómo se puede convertir fácilmente nuestras aplicaciones winforms a wpf y por otro como reaprovechar el máximo código posible. Voy a exponer unas cuantas ideas que quizá os pueden ayudar pero que se resumen en dos: usad una arquitectura n-layer (por cierto que nosotros hablamos sólo de “n-capas” pero no confundais n-layer con n-tier), por un lado y el patrón separated presentation por el otro…

Arquitectura n-layer

En esta arquitectura, básicamente, distribuimos nuestro código en n-capas lógicas, donde n suele tener un valor cercano a 3: la separación clásica es presentación, lógica y datos. Aunque se pueden meter más (o menos, igual podemos prescindir de la capa de datos) capas lógicas (como p.ej. lógica de presentación).

Una arquitectura n-layer nos ayudará a reaprovechar nuestro código de la capa lógica con independencia de la capa de presentación que tengamos… Para ello basta seguir dos reglas que nos indicarán si vamos por buen camino:

  1. Nuestra capa de lógica debe estar en un proyecto separado (una librería de clases). Puede haber (y generalmente habrá) una referencia desde el proyecto que sea la capa de presentación al proyecto que es la capa de lógica pero nunca debe aparecer una referencia a la capa de lógica que vaya hacia la capa de presentación.
  2. El proyecto que contiene nuestra capa lógica no debe tener nunca ninguna referencia a ningún ensamblado de .NET que dependa, directa o indirectamente, de Winforms o WPF… P.ej. si te aparece una referencia a System.Windows.Forms… mal, porque estás ligando tu capa de lógica a una tecnología de presentación.

La comunicación desde la capa de presentación a la capa lógica puede ser acoplada: lógico, disponemos de una referencia en la capa de presentación a la capa lógica y por lo tanto podemos instanciar cualquier clase pública de la capa de lógica y llamar a sus métodos directamente.

La comunicación desde la capa lógica a la capa de presentación debe ser desacoplada: no tenemos otra opción, dado que no podemos tener ninguna referencia a la capa de presentación desde la capa lógica. Aquí tenemos tres alternativas válidas:

  1. Comunicación pasiva: es decir, la capa de lógica se limita a devolver toda la información que la capa de presentación solicita mediante los valores de retorno de los métodos. Así, la comunicación la inicia siempre la capa de presentación.
  2. Eventos, o cualquier mecanismo similar (como commands): Cuando la capa de lógica quiere informar de algo a la capa de presentación, lanza eventos a los que está suscrita la capa de presentación y esta actúa en consecuencia. Esto permite que la capa lógica realice envíos de información a la capa lógica sin ser necesario que esta última inicie la comunicación.
  3. Utilizar interfaces junto con un patrón service locator. Esta alternativa precisa el uso de un contenedor IoC (como Unity o Windsor), así como la aparición de un tercer assembly destinado a contener las interfaces. Usando esta aproximación, las clases de la capa de presentación implementan todas ellas las interfaces (definidas en el assembly aparte) y la capa de lógica obtiene referencias a los objetos de la capa de presentación usando las interfaces y el contenedor de IoC. Esta alternativa permite una conversación “tu-a-tu” con la capa de presentación.

Por supuesto las tres alternativas pueden combinarse.

Esto independiza nuestra capa de lógica (y de datos) de la presentación usada. Si alguna vez queremos migrar nuestra aplicación, p.ej. de winforms a wpf, sólo deberemos reescribir la capa de presentación… lo que según como esté diseñada puede ser un trabajo asumible o una obra de titanes.

Separated Presentation

Bajo este nombre se agrupan multitud de patrones (MVC, MVP, MVVM), pero todos ellos coinciden en lo básico: separa siempre tu código que muestra los datos (el formulario) del código que indica cuando y como debe mostrarlos.

Lamentablemente, desde los tiempos de Visual Basic, Microsoft nos acostumbra a desarrollar usando el “patrón formulario”, que básicamente consiste en crear un form, rellenar-lo de controles y asociar eventos a funciones tipo MyButton_Click() que contendrán todo el código. Este modelo es rápido, fácil de entender y produce buenos resultados… hasta que uno debe empezar a cambiar cosas. A lo mejor al cabo de unos meses en tu aplicación le aparece otro formulario que se parece mucho a un formulario ya existente pero a lo mejor muestra un par de controles más o se comporta ligeramente distinto… O bien haces copy-paste del primer formulario en uno nuevo y lo modificas (funciona pero luego vas a mantener dos formularios parecidos) o empiezas a meter ifs en el primer formulario hasta que todo se vuelve un galimatías.

Créeme: Olvida el “patrón formulario” cuanto antes. Sirve para pequeñas aplicaciones que no precisen un mantenimiento excesivo, pero para aplicaciones mayores usa alguna variante de separated presentation.

Si nos centramos en el caso concreto de Winforms y WPF, una de las mejores elecciones es MVP: si ya sabes que tu aplicación deberá ser migrada (o bien debe ser multi-presentación) entonces MVP te ahorrará bastante trabajo: “básicamente” sólo deberás rediseñar los formularios y podrás aprovechar el modelo y los presenters. Yo por ejemplo suelo usar siempre MVP cuando desarrollo en Winforms (no por temas de migración, sino porque considero que es el patrón que aplica mejor) y cuando estoy en WPF uso MVVM o MVP dependiendo de la ocasión.

Reconozco que si se debiera migrar una aplicación WPF creada usando MVVM a winforms habría bastante trabajo, pero de todos modos muchas veces si se migra una aplicación a una tecnología de presentación nueva es para aprovechar las nuevas capacidades (me cuesta imaginarme una migración de WPF a winforms, mientras que al revés puede ser más habitual).

En fin, migrar una aplicación a otra tecnología de presentación siempre cuesta trabajo, pero si separamos bien las capas (n-layer) y organizamos bien nuestra capa de presentación (separated presentation) tendremos mucho de ganado…

Saludos!!!!

PD: Esto es (para variar) un crosspost de mi blog en geeks.ms!

No hay comentarios: