Una de las cosas que me gusta destacar del lenguaje de programación Xojo, en combinación con la programación orientada a Eventos, es que permite crear prototipos de aplicaciones en tiempo récord, o bien crear aplicaciones completas y finales con un estilo de programación que podríamos denominar como “libre” (es decir, muy relajado desde lo que vendría a ser una OOP más formal).
Ahora bien, la riqueza del lenguaje proporciona recursos que, una vez que aprendemos a emplearlos, nos brindan una flexibilidad superior para hacer aquello que, de otro modo, requeriría de una cantidad de líneas de código notablemente superior, o bien un código sumamente más enmarañado y, por ende, notoriamente más difícil de mantener.
Una de las características que más cuesta entender de entrada puede ser el de los Delegados, y probablemente sea así porque, tal y como me ocurrió a mi inicialmente, no tiene nada que ver con lo que se entiende por el mismo concepto desde otros lenguajes de programación.
En Xojo, el concepto de delegado se corresponde con la definición de un nuevo tipo de dato que podemos crear bien en una clase o en un módulo.
La segunda particularidad es que durante la creación de un delegado observamos que el panel Inspector nos brinda las mismas opciones que las encontradas cuando deinimos un nuevo método: nombre, argumentos y tipo devuelto; siendo tanto los argumentos como el tipo devuelto cuestiones opcionales en la definición.
¿Qué significa esto? Sencillo: la definición del tipo de dato se corresponde con la signatura de un método. Por consiguiente, cuando utilicemos este nuevo tipo de dato posteriormente en la declaración de una variable:
dim miVariable as MiDelegado
Estaremos limitando ‘miVariable’ a que acepte únicamente cualquier otra signatura de método conforme a la definición establecida durante la creación del delegado ‘MiDelegado’.
Cambio de método en tiempo de ejecución
Y con lo anterior es donde vemos la funcionalidad que nos proporcionan los delegados en Xojo: nos permiten cambia de forma dinámica, en tiempo de ejecución, el método apuntado por una variable y, por tanto, invocar uno u otro en función de cuál sea nuestra necesidad.
Esto va más allá de lo que inicialmente puedas ver, dado que cualquier método asignado a la variable declarada como un delegado concreto ¡no tiene por qué pertencener a la misma jerarquía de clase! Por tanto, en función de cuáles sean las necesidades de nuestra aplicación, podremos invocar métodos distintos de objetos distintos creados a partir de clases completamente diferentes.
Y esto da muchísimo juego, requiriendo sólo unas pocas líneas de código y proporcionando la máxima separación de responsabilidades entre los participantes, lo que equivale a un código más sencillo de evolucionar y mantener.
Ahora bien, ¿y cómo podemos asignar nuevos métodos a una variable declarada como un Delegado determinado? La respuesta se encuentra en las instrucciones AddressOf y WeakAddressOf.
Breve inciso sobre la gestión de memoria en Xojo
Seguramente ya sepas que en Xojo no debes de preocuparte de realizar una gestión manual de la memoria utilizada por el programa, sino que un mecanismo denominado Recolección de Basura es el encargado de realizarlo de forma automática atendiendo a los diferentes estadios en la creación, uso y destrucción de los objetos utilizados durante la ejecución de una secuencia de código determinada.
Ahora bien, cuando se trata de almacenar referencias a otros objetos en una variable puede ocurrir lo que, en programación, se denominan referencias circulares: un objeto retiene a otro que, a su vez, retiene al primero; de modo que no se puede liberar la memoria consumida por ambos produciéndose una ‘fuga de memoria’.
Este es el tipo de situaciones con las que hemos de tener cuidado al utilizar AddressOf para obtener la dirección o ubicación en memoria en la que se encuentra almacenado un objeto determinado (en este caso el método del objeto que lo contiene), dado que se produce una referencia fuerte (se incremente en uno el uso de un objeto).
Bajo el anterior escenario, si por un casual el objeto que guarda la referencia de un método obtenido mediante AddressOf es el mismo que la dirección obtenida, se producirá una referencia circular: ambos se “apuntan” entre sí.
Para evitar este tipo de situaciones es cuando hemos de utilizar la instrucción WeakAddressOf y que, como su nombre deja entrever, se encarga de guardar una referencia débil hacia el objeto apuntado; de modo que cuando el recolector de basura deba de liberar la memoria ocupada por el objeto encargado de guardar la referencia no se producirá la tan temida referencia circular.
Guardar e invocar un método
Por tanto, y con lo visto hasta ahora, ya disponemos del conocimiento básico necesario para lidiar con la creación y asignación de un método en tiempo de ejecución a una variable declarada como un tipo de Delegado creado por nosotros:
Dim d as MiDelegado d = AddressOf miMetodo
o bien
d = WeakAddressOf miMetodo
Por último, cuando llegue el momento, ya solo nos quedará invocar el método almacenado por la variable de tipo delegado para que se ejecute el código asociado a dicho método:
d.invoke
Dicha sintaxis funcionará en el caso de que el delegado no tome parámetros y tampoco devuelva ningún tipo; pero pongamos por caso que la definición del Delegado en cuestión tomase dos parámetros de tipo entero. Entonces la invocación sería del siguiente modo:
d.Invoke(10,20)
Y si en la definición del delegado también se incluye la devolución de un tipo de dato, entonces haremos lo propio a la hora de utilizar Invoke sobre la variable que contiene el método:
dim resultado as integer resultado = d.Invoke(10,20)