La sencillez de una interfaz compleja

Hace algún tiempecillo escribí un artículo para el e-zine de raona, que enviamos a distintos clientes. En el artículo esbozaba los patrones básicos para diseñar interfaces de usuario compuestas. Posteriormente me surgió la idea de que una ampliación de dicho artículo, donde se mostrasen ejemplos en PRISM y WPF de estos conceptos podría ser interesante. Afortunadamente en DotNetMania pensaron lo mismo y es por ello que en la revista de este marzo hay un artículo con este mismo título.

Lo que ahora sigue és el artículo original, el que escribí para el e-zine. Aunque el de DotNetMania describe las mismas ideas, ambos artículos tienen poco a ver (tanto en contenido, como en enfoque como en extensión). Como creo que el artículo del e-zine tambiém tiene su interés, me he tomado la libertad de postearlo aquí 🙂

PD: Por si os interesa, los distintos e-zines que vamos sacando en raona, los podeis consultar en la sección de ezines de raona.

La sencillez de una interfaz compleja (versión e-zine).

Cada vez las aplicaciones son más y más complejas, y los usuarios son más y más exigentes. Los desarrolladores debemos afrontar este doble reto siendo capaces de proporcionar mejores y más complejas aplicaciones, en cada vez menos tiempo. Evidentemente este es un proceso global, que afecta a todo el desarrollo de una solución de software, pero me gustaría centrarme en los retos concretos que esto implica cuando hablamos de interfaces de usuario. No en vano, la interfaz de usuario es el punto de conexión entre el usuario y la aplicación. Es un elemento crítico y de su implementación depende en buena medida el éxito final de toda la aplicación.

Interfaces complejas

Las aplicaciones actuales demandan interfaces de usuario complejas a nivel técnico pero que a la vez sean sencillas de utilizar y de mantener. Que se adapten a cualquier tipo de usuario, que tanto usuarios expertos como inexpertos se sientan a gusto utilizándola. En resumen que la experiencia del usuario al utilizar la interfaz sea plena y que nuestra aplicación sea usable por cualquier usuario.

El término interfaz compleja, no quiere referirse al aspecto visual o usable si no al hecho de que cada vez es más complejo y difícil realizar interfaces que permitan conjugar las crecientes posibilidades de la aplicación con la sencillez de uso y la usabilidad. Este es el reto que debemos solucionar.

Nuevas tecnologías, nuevas posibilidades

Con la aparición de WPF, Microsoft ha dotado a los desarrolladores de un montón de nuevas posibilidades para la construcción de interfaces. El hecho de disponer de un lenguaje declarativo (XAML) para definir las interfaces permite la separación entre dos roles básicos hoy en día: los diseñadores y los desarrolladores. Pero separar desarrolladores y diseñadores es sólo un primer paso (un primer gran paso) pero no soluciona la problemática de fondo: el modo en cómo están diseñadas y desarrolladas las interfaces de usuario.

El mecanismo habitual de desarrollar interfaces de usuario en .NET, viene influenciado de los tiempos de Visual Basic. Es un patrón que consiste en una clase contenedora (llamada comúnmente formulario) que contiene a los controles y toda la lógica asociada a dichos controles. Por norma general el formulario se suscribe a los eventos de los controles a los que contiene y realiza la lógica apropiada en las funciones gestoras de dichos eventos. La ventaja de este modelo de programación es que es sencillo, intuitivo y fácil de aprender. Las desventajas son que al tener unidas la lógica con la presentación se vuelve más complejo el mantenimiento tendiendo a colapsar en grandes proyectos y se dificulta la separación del desarrollo entre varios equipos.

Una de las claves para desarrollar interfaces de usuario complejas y que resulten mantenibles es desacoplar la presentación de la lógica de dicha presentación. WPF presenta algunas novedades que ayudan a conseguir dicho desacople:

  1. Commands: Permite desacoplar los eventos de los controles de la gestión de dichos eventos y unificar en un mismo punto la gestión de distintos eventos de distintos controles pero que realizan la misma acción (p.ej. un botón de una toolbar y una entrada de menú).
  2. Routed Events: Permiten tratar en un solo contenedor cualquier evento generado por cualquier de los controles “hijos” (estén en el nivel de profundidad que estén).

Estas novedades aunque interesantes, por si solas no son suficientes para desacoplar totalmente la presentación y su lógica. Para ello hace falta un cambio de paradigma, un nuevo modelo.

Composite Application Library

Composite Application Library (CAL) también conocida por su nombre en código PRISM, es un framework lanzado por la gente de “Patterns & Practices” para ayudar en la construcción de aplicaciones complejas utilizando WPF.

CAL se basa en varios patrones para ayudar a conseguir un desacople total entre la presentación y la lógica de presentación. A continuación comentaremos tres de los patrones más relevantes de CAL.

Inversion of Control (IoC)

Este patron desacopla una clase en concreto de otra clase de las clases que utiliza, es decir de sus referencias:

Dependencias entre clsases

La clase ClassA utiliza dos servicios (ServiceA y ServiceB) y por lo tanto tiene referencias directas a ellos. Esto implica, entre otras cosas, que si queremos modificar las referencias debemos modificar el código fuente de ClassA. Para desacoplar la clase de sus referencias la solución es añadir un componente externo que se encargue de encontrar y gestionar las referencias de la clase ClassA. En función de cómo diseñemos este elemento externo hablaremos de un patrón ServiceLocator o de Dependency Injection.

Service locator &  Dependency Injection

Con ServiceLocator la clase ClassA sólo contiene una referencia al Locator siendo esta clase la que obtiene y devuelve la referencia adecuada a la clase. El Locator NO instancia los servicios, simplemente los localiza (los servicios han estado instanciados previamente por alguien): mientras un servicio esté registrado, el Locator lo encontrará. CAL utiliza Unity para ofrecer un Service Locator, lo que nos permite instanciar y registrar servicios que luego serán utilizados en cualquier parte de la aplicación.

Con Dependency Injection, existe un Builder que crea el objeto del servicio y lo “incrusta” en alguna propiedad específica de la clase ClassA (o bien en algún parámetro del constructor). CAL utiliza Unity para proporcionar Dependency Injection.

Separated Presentation

Si IoC nos permite desacoplar nuestras clases de las referencias que necesitan, el conjunto de patrones conocidos como “separated presentation”, nos permiten separar la presentación de la parte de la lógica que gestiona la presentación.

Existen varios patrones que implementan “separated presentation” siendo los más concidos MVP (Model – View – Presenter) o MVVM (Model – View- ViewModel). Debido a las capacidades de WPF se tiende a utilizar más MVVM (mientras que MVP es más frecuente en aplicaciones Windows forms p.ej), debido a que MVVM se basa en uno de los aspectos más potentes de WPF: su data binding.

El patrón MVVM se basa en tres elementos:

  • Vista (View): Contiene los elementos visuales (controles). En el caso de WPF suele estar creada por un diseñador e implementada vía XAML
  • Modelo (Model): Contiene los datos que muestra la vista. En este punto es cuando se utiliza básicamente el data binding, para enlazar los valores de los controles de la vista a valores de distintos elementos del modelo.
  • Modelo de Vista (ViewModel): En interfaces sencillas, la vista se puede enlazar directamente con el modelo, pero en interfaces complejas no suele ser posible. En muchos casos el Modelo puede tener multitud de información que no se puede mapear a valores de los controles o bien nuestra vista debe realizar y mantener valores “temporales” que no forman parte estricta del modelo. Es en estos casos donde entra en escena el “ViewModel”, actuando de puente proporcionando transformadores para poder enlazar los controles de la vista y commands para que la vista pueda interactuar con el modelo.

De esta manera no existe más una clase “formulario” que contiene los controles y toda la lógica asociada a ellos.

Event Aggregator

Un Event Aggregator es la implementación de un modelo de eventos publish and subscribe, donde cuando alquien quiere recibir un evento se suscribe a este tipo de evento en particular. Cuando alguien quiere enviar un evento lo publica y el event aggregator se ocupa de que todos los que se hayan suscrito a este tipo de evento lo reciban. De esta manera se desacoplan totalmente los publicadores de eventos de sus suscriptores.

Conclusiones

Para desarrollar interfaces complejas que a la vez sean amigables para el usuario y mantenibles para el desarrollador es necesario involucrar a los diseñadores por un lado y desacoplar la presentación de la lógica que la gestiona por otro. Gracias a XAML, WPF permite que los diseñadores se integren totalmente en el ciclo de desarrollo, y el uso de determinados patrones permite desacoplar los distintos elementos que conforman la inferfaz de usuario. CAL es un framework y una guía de estilo que basándose en dichos patrones y en las funcionalidades inherentes a WPF nos permite desarrollar aplicaciones complejas que sean mantenibles y amigables a la vez.

Referencias

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *