¿Podemos establecer alguna conexión entre estos dos mundos? Si nos detenemos por un instante veremos que comparten muchas más cosas de las que a simple vista podría parecer:
- Son lenguajes de tipado estático.
- Ambos producen código compatible con la máquina virtual de Java (JVM)
- Las funciones de alto nivel de Scala comparten algunas características con los advices de AspectJ
- Los traits de Scala comparten algunas características con el static crosscuting de AspectJ.
Durante el resto de la entrada que nos ocupa (y la siguiente) realizaremos una pequeña comparativa en la que pondremos de manifiesto el modo en que cada uno de estos lenguajes resuelve determinado tipo de problemas. Pongámonos manos a la obra.
Cachés
A través de este ejemplo se pretende presentar el modo en que ambos lenguajes solucionan el problema de aplicar una funcionalidad transversal en un punto determinado del código. Implementaremos un mecanismos de caché extremadamente sencillo de manera que podamos centrar nuestra atención en los aspectos relevantes de las alternativas que estamos planteando
En el caso de AspectJ se está tendiendo a utilizar las anotaciones como un elemento de marcado (y parece que está siendo recibido con una aceptación más que notable). Nuestro primer paso será definir la anotación con la que realizaremos el "marcado" de aquellos métodos que deseamos establecer como cacheados:
@Retention(RetentionPolicy.RUNTIME)
public @interface Cachable {
String scriptKey () default "";
}
El atributo keyScript de la anotación anterior actuaría (en una caché real), como un pequeño lenguaje de scripting de manera que pudiera ser evaluado por la caché para generar la clave bajo la que se almacenará una determinada llamada (obviaremos esta parte para intentar no distraernos de nuestro principal objetivo).
Una vez definido el elemento de marcado, definiremos nuestro aspecto, el cual será capaz de capturar las llamadas de aquellos métodos anotados con @Cacheable y realizar la lógica necesaria de nuestra sencilla caché
En el caso de Scala llevaremos a cabo nuestra funcionalidad transversal mediante el uso de funciones de primer nivel (high order functions)
Gracias a la funcionalidad anterior, podemos pasar al método cache una función que será ejecutada en caso de ser necesario, devolviendo los valores de la caché en el supuesto de que haya sido calculado en un paso anterior.
Si comparamos las dos alternativas que hemos planteado hasta este momento:
- En el caso de AspectJ, cada uno de los elementos que deseamos cachear tendremos que anotarlos con, valga la redundancia, la anotación que hemos definido anteriormente. Mientras tanto, en el caso de Scala, tendremos que recubrir cada uno de los métodos que deseamos cachear con la función de alto nivel (high order function).
- En el caso de AspectJ estamos utilizando un "lenguaje" externo que actua como elementos de las claves de nuestra caché mientras que en el caso de Scala estamos utilizando el propio lenguaje de manera nativa, con el consiguiente beneficio que ello conlleva.
Esta ha sido nuestra primera aproximación a Scala y AspectJ. Durante las siguientes entradas analizaremos algunos ejemplos adicionales y veremos como podemos integrar lo mejor de ambos mundos.
Podéis encontrar el código fuente de los ejemplos anteriores en el siguiente repositorio de GitHub, concretamente en los proyectos AspectJCacheExample y ScalaCacheExample.
Hasta pronto!
Hasta pronto!
No hay comentarios:
Publicar un comentario