Introducción a la programación dirigida por eventos



La programación dirigida por eventos es un paradigma de programación en el que el flujo del programa está determinado por eventos o mensajes desde otros programas o hilos de ejecución.

Las aplicaciones desarrolladas con programación dirigida por eventos implementan un bucle principal o main loop donde se ejecutan las dos secciones principales de la aplicación: El selector de eventos y el manejador de eventos.

La mayoría de librerías para el desarrollo de aplicaciones con GUI como GTK o Qt están diseñados para ser dirigidos por eventos, de ahí el famoso sistema de ranuras y señales de Qt que no es más que un patrón de diseño observer bastante currado.

Un poco de historia

A finales de los 70, los sistemas estaban pensados para trabajar como cadenas de ensamblaje donde un programa usaba una entrada y producía una salida que sería utilizada por otro programa como entrada para producir otra salida y así hasta finalizar el proceso. Este proceso mental de construir software es la base del desarrollo estructurado.

El padre del desarrollo estructurado (que no de la programación estructurada ojo) fue Larry LeRoy Constantine bajo el ala del Instituto de Investigación de Sistemas de IBM. Uno de los mayores expertos y defensores de los métodos estructurados es Edward Yourdon, tanto que las expresiones “Yourdon” y “métodos de análisis y diseño estructurado” son sinónimos.

Constantine y Yourdon definieron nuevos modelos de control del flujo de datos implementando lo que llamaron transacciones que en realidad son un patrón de diseño de manejadores de eventos.

En el diagrama de la derecha se muestra el siguiente proceso:

  • Un flujo de datos invoca eventos o lo que Constantine y Yourdon llamaron “transacciones

  • Un disparador los envía a manejadores especializados, Constantine y Yourdon lo llamaron “centro de transacciones

  • un conjunto de manejadores que se encargan de realizar operaciones sobre el flujo de datos

El trabajo del disparador es analizar los eventos para determinar su naturaleza y entonces enviarlos al manejador adecuado que es capaz de trabajar con eventos de esa naturaleza. El disparador tiene que procesar un flujo de eventos, así que su lógica debe incluir un bucle de eventos para poder enviar un evento a un manejador y volver a escuchar a la espera de nuevos eventos que disparar.

Es común que exista un evento especial que rompa el bucle y salga de la aplicación, a ese evento se le llama evento finalizador y es muy común en todas las librerías para escribir aplicaciones GUI. También puede ocurrir que el disparador capture un evento de naturaleza desconocida o para el que no exista un manejador adecuado, en esos casos, el disparador debe descartar el evento o lanzar una excepción.

En algunas ocasiones es común que el disparador y los manejadores no sean capaces de procesar los eventos con la suficiente premura conforme van llegando por lo que la mayoría de aplicaciones basadas en GUI implementan una cola de eventos. Esta lógica es muy delicada y si no se implementa bien puede ser producto de cuellos de botella fatales.

Con la aparición en los noventa de la programación orientada a objetos nuevos diseños vieron la luz como ESA (Essential Systems Analysis) y JSD (Jackson System Development) que rompieron de forma brutal con la antigua manera de pensar en métodos estructurados.

Arquitectura cliente-servidor

La arquitectura cliente-servidor es claramente una implementación del patrón de diseño de manejadores de eventos. Podemos pensar en el servidor como un disparador de eventos que espera a la escucha de una petición de un cliente que envía un evento y entonces el servidor lo re-envía a un manejador que lo procesa y devuelve un resultado.

El servidor implementa el bucle de eventos que escucha de forma continua y procesa las peticiones reenviándolas a partes más especialistas del servidor donde son procesadas y devueltas al cliente como respuesta. Si el bucle de eventos recibe una petición extraña, sencillamente la descarta y emite un mensaje de error al cliente emisor.

Programación GUI

La programación gráfica o programación de GUI es dura, sobre todo si vienes del desarrollo web. Para empezar, todo elemento que conforma la interfaz de usuario de la aplicación debe de ser definido con detalle. Su posición, tamaño, color, aspecto, comportamiento, etc, etc. Por eso existen los IDEs y los Frameworks, para hacernos la vida más sencilla a los desarrolladores.

En segundo lugar, la programación con GUI es difícil por que existen muchos detalles y conceptos que dependen completamente del manejo de eventos. Cualquier Widget en la interfaz de usuario tiene uno o más eventos asociados y a su vez emite uno o más eventos hacia el mundo exterior. El hardware de entrada como el ratón y el teclado, también generan eventos y esos eventos también pueden ser capturados por los widgets de la interfaz de usuario.

La mayoría de herramientas de creación de interfaces gráficas de usuario se distribuyen como frameworks. Los frameworks implementan el bucle de eventos y la cola de eventos para que no tengamos que implementarlos nosotros mismos. Eso está bien, pero el mecanismo queda encerrado dentro del framework y si no estamos acostumbrados a la programación dirigida por eventos algunos conceptos pueden resultar realmente confusos.

Y no solo esos bucles quedan ocultos dentro del funcionamiento del framework, sino que también quedan ocultas mecánicas básicas que es necesario comprender para llegar a tener un entendimiento claro de la magia detrás de este paradigma. Un ejemplo claro es el patrón observer.

El patrón Observer

La idea principal detrás del patrón observer es que existe una entidad con estados cambiantes y una o más entidades observándola. Los observadores esperan a que la entidad observada les informe de un cambio de estado a través de un evento que puede ser de su interés, por lo que los observadores se registran con la entidad observada.

Cuando ocurre un evento, la entidad observada mira en su lista de observadores y notifica a aquellos que se registraron para recibir eventos de ese tipo. Los observadores dejaron instrucciones detalladas de como puede la entidad observada ponerse en contacto con ellos para recibir los eventos. A este patrón también se le llama El Principio de Hollywood como parodia de la típica frase de Hollywood “te llamaremos en cuanto tengamos un papel para ti“.

El sistema de ranuras y señales de Qt y el sistema de señales de GTK son ambos implementaciones del patrón de diseño Observer.

Eventos y MVC

En el patrón de diseño MVC los eventos y el patrón de diseño observer conforman el núcleo duro del funcionamiento interno del sistema. En en patrón de diseño MVC, el Modelo es un objeto que gestiona los datos y el comportamiento de la aplicación, es la entidad observable en el patrón observer.

Las Vistas se registran con el Modelo como observadores del mismo. Cuando el Controlador realiza cambios en el Modelo, notifica esos cambios a sus observadores registrados que modifican la interfaz de la aplicación.

En el patrón de diseño MVC los objetos de evento incluyen grandes cantidades de datos que viajan de una parte a otra de la aplicación a través del viejo y raído patrón de manejadores de eventos.

Programación asíncrona

La programación asíncrona pretende llevar este patrón de diseño a todos los aspectos del desarrollo de software por medio de mecanismos que articulan de una u otra forma el uso de eventos, señales y callbacks en todas las llamadas a funciones y métodos llevados a cabo por la aplicación utilizando además múltiples hilos de ejecución normalmente en una pila o piscina de hilos gestionada por un bucle de eventos extremadamente complejo.

Algunos lenguajes como .NET lo incorporan como parte del lenguaje de facto (versión 3.0 o superiores). Otros lenguajes disponen de librerías, herramientas o frameworks que implementan la programación asíncrona. Así tenemos Twisted para Python, EventMachine para Ruby, Node.js para JavaScript, y muchas otras que seguramente desconozco para muchos otros lenguajes.

En esta pequeña introducción a la programación dirigida por eventos hemos hecho un repaso por la historia de este paradigma de programación que lleva con nosotros mucho tiempo aunque para algunos es casi desconocido. En próximas entregas hablaremos sobre las diferentes herramientas a nuestra disposición a la hora de programar de forma asíncrona en escenarios donde se requiere de un tiempo de respuesta y rendimiento superior a lo normal.

Portada de Genbeta