Autor: Cristóbal González Almirón.
Fecha: Septiembre de 2005.
Introducción a la
“Programación Orientada a Aspectos”_ 1
3 ¿Qué es la
programación orientada a aspectos?_ 2
4 ¿Cómo surge la
programación orientada a aspectos?_ 2
5 Caso práctico: acceso
a una base de datos 2
6 Introducción de los
requisitos adicionales: los aspectos 3
7 Solución general para
este problema 3
8 Adaptar el código para
la programación orientada a aspectos 5
10 Escenarios
posibles para la programación orientada a aspectos 6
11 Ventajas y
desventajas de cada método_ 6
12 Introducción a
los aspectos en lenguaje DE_ 7
Introducción a la “Programación Orientada a
Aspectos”
1
Resumen
Este articulo introduce la programación
orientada a aspectos. La programación orientada a aspectos soluciona la
introducción dentro de un desarrollo de las funciones auxiliares, mediante el
uso de código de crosscutting o código ortogonal, que son funciones que se
repiten a lo largo del código y que se encargan de implementar los requisitos
adicionales auxiliares, por ejemplo de seguridad, monitorización, gestión del
logs, etc.
Se introducen los tres modelos de
introducción de los aspectos: a posteriori (refabricación “refactoring”), en
diseño y a posteriori.
Se ilustra todo con un caso práctico:
“introducción de los aspectos en una consulta a base de datos”.
Por último se incluye una reseña a la
programación de aspectos en lenguaje DE.
2
Introducción
La introducción de la programación
orientada a objetos en la década de los 90 introdujo una nueva metodología de
desarrollo, que permitió simplificar el desarrollo de proyectos complejos
utilizando técnicas de programación nuevas. El concepto de clase como unidad de
información y código permite aislar funcionalidades dispersas en unidades de
programación muy definidas. Este tipo de diseño creó el concepto de componente
reutilizable, más potente que el de biblioteca de funciones tradicional de la
programación estructurada.
Los nuevos diseños agrupan en las clases
las funciones específicas, aislando en cada clase las funciones que realizan
cambios sobre cada tipo de datos.
Sin embargo, hay una serie de funciones
auxiliares, comunes a múltiples partes del programa, que se resisten a ser
incluidas dentro de una clase concreta. Son funciones que son
introducidas en cada módulo para realizar tareas ajenas a la función principal
de cada módulo. El ejemplo más típico es el código de Log. A estos códigos
auxiliares se les denomina “aspectos”.
La programación orientada a objetos no
facilita la inserción de los aspectos en el programa, ya que la función de los
aspectos no es específica de un tipo de datos o de una función de una clase,
sino que es común a varias de ellas.
Al igual que la programación orientada a
objetos se ocupa de agrupar en módulos de clases las funciones específicas, la
programación orientada a aspectos se ocupa de agrupar en módulos las funciones
comunes, y de gestionar su despliegue en el resto de módulos.
3
¿Qué es la programación orientada a aspectos?
La programación orientada a aspectos se
ocupa de implementar aquellas funciones que son genéricas del sistema a
desarrollar y no específicas. Dentro de estas funciones se incluyen por ejemplo
las funciones de seguridad, monitorización, log de eventos.
4
¿Cómo surge la programación orientada a aspectos?
El proceso en el que aparecen los
aspectos en el diseño de un programa es sencillo. Tras realizar el análisis de
requisitos de las funciones a incluir, se introducen una serie de requisitos
auxiliares que además se deben cumplir, y que normalmente se deben incluir de
forma repetitiva en los desarrollos similares.
5
Caso práctico: acceso a una base de datos
Un ejemplo típico podría ser el proceso
de alta de un producto en un sistema de aprovisionamiento. El responsable del
análisis recibe la petición de añadir esta funcionalidad a la aplicación y
extrae los siguientes requisitos básicos:
·
El módulo se debe recoger la información del producto
a dar de alta
·
Con esta información se conectará a la base de datos
·
Luego comprobará si ese producto existe, realizando
la consulta inicial a la base de datos.
·
Si no existe y se puede dar de alta, realizará
la modificación en la base de datos.
·
Además hará las modificaciones adicionales que
provoque el cambio.
·
Por último informará del éxito del proceso al usuario
y finalizará.
Este análisis es bastante genérico.
Dependiendo de la complejidad de la aplicación tendríamos un conjunto de
servicios semejantes para consultas, altas, bajas, modificaciones y procesos
adicionales, probablemente uno por cada tipo de producto definido en la
aplicación.
El seudo código para esta función podría
ser:
Funcion CrearProductoTipoA {
Recoger_Informacion_Usuario
Conectar_Base_Datos
Comprobar_Existencia_Producto
Dar_De_Alta_Producto
Realizar_Cambios_Adicionales
Informar_Usuario_Y_Finalizar
}
6
Introducción de los requisitos adicionales: los
aspectos
Pero cada una de las funciones definidas
normalmente tiene que cumplir una serie de requisitos adicionales, que
normalmente se repiten para todos ellos. Por ejemplo antes de iniciar la
conexión con la base de datos, se debe comprobar si el usuario tiene privilegios
suficientes para realizar la operación. Una vez conectado a la base de datos,
todo el proceso debe dejar una serie de logs en el sistema, que serán más o
menos complejos dependiendo de si la aplicación está en modo de depuración o
simple explotación. Además, al finalizar el trabajo se debe enviar un informe o
mensaje al sistema de monitorización.
A estas funciones auxiliares que se
repiten a lo largo de todos los análisis de los diferentes módulos de la
aplicación se les denomina aspectos.
7
Solución general para este problema
El método general para resolver esto
consiste en agrupar las funciones auxiliares en bibliotecas de funciones, que
se ocuparán del tratamiento genérico de estas situaciones. Así se podría crear
una biblioteca con las siguientes funciones:
·
Comprobar privilegios de usuario. Funciones para
comprobar que dado un usuario y la operación a realizar, el usuario tiene el
privilegio suficiente para realizarla. Devuelve OK/Error.
·
Escritura en el log. Dependiento del nivel de detalle
del log, iría escribiendo los mensajes correspondientes en el log de la
aplicación.
·
Monitorización. Informaría al sistema de
monitorización de las etapas por las que va pasando el proceso. El sistema de
monitorización podría detectar situaciones anómalas, como procesos que toman
más del tiempo necesario para su ejecución.
El programador sólo tiene que introducir
las llamadas a función correspondientes dentro del código, prácticamente
utilizando la técnica de “copiar y pegar”. En la literatura de la programación
orientada a aspectos, a este código se le denomina código de “cortar a través”
(crosscutting),
Como ejemplo, se podría reformar el
seudocódigo para introducir los puntos de inserción, que marcaremos entre
corchetes:
// pseudocódigo
Funcion CrearProductoTipoA {
Recoger_Informacion_Usuario
[Comprobacion_Privilegios]
[Comienzo_Monitorizacion]
Conectar_Base_Datos
[Log_Conexion_OK]
Comprobar_Existencia_Producto
Dar_De_Alta_Producto
[Log_Alta_Producto_TipoA]
Realizar_Cambios_Adicionales
[Log_Cambios_Producto_TipoA]
Informar_Usuario_Y_Finalizar
[Fin_Monitorizacion]
}
8
Adaptar el código para la programación orientada a
aspectos
Básicamente, introducir aspectos en el
código fuente se reduce a insertar puntos de inserción, donde más adelante se
insertará, probablemente con una herramienta externa o manualmente, las
funciones de crosscutting.
Hay dos posibles formas de abordar el
problema. La primera es anticipara en el diseño que este código de aspectos se
va a utilizar. En este caso, al diseñar las funciones, clases o métodos se
tiene en cuenta esta posibilidad. En este caso se pueden insertar los puntos de
inserción a lo largo del código de las funciones, para luego más adelante
utilizar las herramientas de inserción.
La posibilidad más frecuente es que no se
haya previsto esta situación, es decir, el código no se ha preparado para
insertar los aspectos. Esto obliga a rescribir y probar todo el código. Es
probable incluso que haya que suprimir código superfluo, que queda obsoleto al
utilizar el código genérico. También puede ocurrir que código introducido en
algunos módulos sea elevado a la categoría de “genérico” en una segunda
revisión del proyecto.
9
Desarrollo paralelo
Otra posibilidad que se debe contemplar
es la posibilidad de desarrollar en dos etapas, o en paralelo. En una primera
etapa se desarrollan las funciones básicas y en la segunda se introducen las
funciones auxiliares. El desarrollo más eficiente sería el que permita realizar
ambas tareas simultáneamente sin interferir una en la otra.
Por ejemplo, se podría utilizar dos
desarrolladores. El primero crea el código de las funciones básicas, como son
las altas, bajas, modificaciones y consultas. Otro desarrollador independiente
crea todo el conjunto de funciones auxiliares tomando como modelo una de las
funciones del primer desarrollador. Ambos desarrolladores prueban sus
desarrollos por separado. Finalmente, se crea un proyecto unión de los dos
anteriores, en el que a cada función básica se le añaden las funciones
auxiliares.
En este caso lo ideal sería el siguiente
desarrollo:
·
El primer desarrollador crea las funciones básicas,
creando una biblioteca de módulos u objetos, con todas las funciones básicas
requeridas en ellos. Este desarrollador debe preparar el código para luego
incluir las funciones auxiliares. Se deben crear los puntos de inserción
correspondientes en todas las funciones básicas.
·
Se prueba todo el conjunto de funciones básicas y se
crea la primera versión, que no incluye los aspectos adicionales (log,
monitorización, seguridad, etc).
·
El segundo desarrollador crea la biblioteca de
funciones auxiliares, y se añaden y prueban en una función básica.
·
Se prueba todo el conjunto de funciones auxiliares.
·
Se crea una versión segunda de las funciones básicas,
pero ahora se añaden las funciones auxiliares.
·
Se prueba esta versión, el resultado debe ser el
mismo que sin introducir las funciones auxiliares.
10
Escenarios posibles para la programación orientada a
aspectos
De lo visto hasta ahora se puede
clasificar los escenarios posibles en tres grupos:
·
Introducción a posteriori de los aspectos. Este es el escenario más común, ya que la mayor parte de las
aplicaciones no están preparadas para insertar aspectos sin modificar el código
fuente. En este escenario es habitual la refabricación (refactoring) del
código, que incluye desde extraer partes del código fuente original a funciones
auxiliares hasta modificar dicho código para que soporte las nuevas
funcionalidades auxiliares.
·
Introducción de los aspectos en tiempo de diseño. Este escenario se presenta en los nuevos desarrollos que tienen
en cuenta los aspectos. Aquí en los requisitos iniciales ya aparecen los
aspectos, por lo que el desarrollo ya los tiene en cuenta, insertando en el
código fuente los puntos de inserción de aspectos (crosscutting).
·
Introducción de los aspectos a priori. Este escenario implica un cambio en las herramientas de
programación actuales, ya que exige que se pueda modificar el código objeto sin
modificar los códigos fuentes. Para ello en el código fuente se marcan los
posibles puntos de crosscutting con etiquetas especiales. Esto permitirá en una
compilación posterior introducir los aspectos. Hay dos métodos básicos para
introducir los aspectos en este caso: uno es utilizar herramientas de
compilación y enlazado que realicen automáticamente la inserción de los
aspectos (por ejemplo, AspectJ para Java) y otra es mediante una modificación
en el propio lenguaje de programación. Este último enfoque es el que utiliza el
lenguaje DE.
11
Ventajas y desventajas de cada método
El primer método no tiene ventajas. Se
introducen los aspectos porque ya no hay otra solución. El número de cambios
que hay que introducir en el código original depende del propio código, las
bibliotecas de aspectos a usar, el lenguaje utilizado y otros factores
diversos.
En el segundo método, los aspectos ya
aparecen entre los requisitos, por lo que se puede utilizar ya el método de
desarrollo en paralelo, aislando el código de aspectos. Este método es similar
al de introducir funciones de traza y depuración en el código fuente. Ya que
los requisitos de aspectos se establecen a priori se pueden utilizar las
herramientas de la programación OO para implementar los aspectos.
Este método tiene como desventaja en que
si aparecen nuevos requisitos de aspectos no contemplados en el diseño
original, volvemos a estar en el primer caso, por lo que debemos aplicar
entonces de nuevo el primer método.
El tercer método es el más efectivo. Sus
ventajas son numerosas, ya que se adaptan a los nuevos requisitos de aspectos
sin modificar el fuente original. Es el método más flexible, pues retrasa la
inserción de los aspectos hasta etapas del desarrollo más avanzadas. También
permite dividir el desarrollo introduciendo los aspectos por separado. Los
nuevos lenguajes de programación semiinterpretados (Java y .NET, por ejemplo)
permiten la emisión de código objeto al vuelo, así como la intercepción y
modificación de código en tiempo de ejecución, lo que permite introducir los
aspectos a posteriori.
Para introducir los aspectos en el tercer
método se siguen estos tres pasos:
1. Inserción de las etiquetas de crosscuting. En este páso el código
fuente original es decorado con las marcas de inserción de código auxiliar. Una
recompilación del código fuente debe producir el mismo código objeto origina,
dejando inalterado el comportamiento del código objeto.
2. Creación del código auxiliar de aspectos. Este código se crea como
funciones auxiliares, probablemente en una biblioteca de código genérica.
3. Inserción de los aspectos en el código objeto. Para ello las
herramientas de desarrollo crean una nueva versión del código objeto que
incluye los aspectos dentro del código objeto.
La introducción de las etiquetas de
inserción requiere una planificación del código fuente más detallada, ya que
hay que decidir aquellas partes del código que pueden ser susceptibles de
insertar los aspectos. Es fundamental que la introducción de estas marcas sea
muy fácil e intuitiva para el desarrollador. Lo ideal es que la propia
inserción de las etiquetas sea transparente para el desarrollador, pasando a
formar parte de sus hábitos de programación.
12
Introducción a los aspectos en lenguaje DE
De lo expuesto hasta ahora, la
introducción de aspectos en un lenguaje debe aportar las siguientes ventajas:
·
Debe simplificar el desarrollo, no complicarlo. Una
solución simple es mucho más efectiva que un conjunto de cambios en la
estructura típica de los programas. La sintaxis utilizada para incluir los
puntos de crosscutting debe integrarse lo más suavemente posible con el estilo
general del lenguaje a utilizar.
·
Debe ser muy intuitiva. Al programador le debe
parecer fácil y natural insertar los puntos de crosscutting dentro de su
aplicación.
·
Debe anticiparse a los posibles cambios de diseño y a
los aspectos añadidos posteriormente. Lo ideal es que el desarrollador pueda
añadir los aspectos a posteriori sin modificar una sola línea de código fuente.
·
Debe respetar la funcionalidad principal del
programa. Los aspectos añaden nuevas funcionalidades al programa, pero a su vez
el programa debe cumplir las funcionalidades ya incluidas.
·
Debe permitir generar versiones con y sin aspectos de
una forma muy sencilla, para poder probar ambos desarrollos.
Todas estas características se han tenido
en cuenta en el diseño del nuevo lenguaje de programación DE, actualmente en
fase de definición (2005). En este lenguaje, los puntos de inserción de los
crosscuttings aparecen de forma natural al utilizar las regiones y las
funciones de prolog y epilog. La sintaxis es una extensión muy sencilla de la sintaxis
de C++, y muy flexible. El programador se acostumbra intuitivamente a generar
posibles puntos de crosscutting, dividiendo el código en pequeñas bloques de
funciones, que más tarde se pueden utilizar para incluir los aspectos.
En el lenguaje DE, la sustitución de
clases por clases derivadas mediante el mecanismo de alias de clases, permite
con un simple reenlazado generar versiones con o sin aspectos, lo que
simplifica el proceso de pruebas. Este mecanismo introduce además la
programación incremental, que añade funcionalidad a las clases poco a poco,
permitiendo la verificación de las funcionalidades introducidas en cada paso
por separado. Pero esto será tema de otro artículo.
Además, la depuración no se convierte en
un laberinto ya que el flujo del programa queda claramente establecido para
cada versión, en tiempo de compilación. Dado que el código objeto final incluye
los aspectos, se puede depurar del modo habitual, sin herramientas específicas.
Este enfoque es más sencillo que la inserción del código al vuelo, mediante
herramientas de entrelazado, como es el caso de AspectJ.
13
Conclusión
La programación orientada a aspectos es
uno de los siguientes pasos dentro del desarrollo de aplicaciones modernas.
Entender este modelo de programación permite generar aplicaciones más fiables y
fáciles de mantener. Por ello es recomendable comenzar a integrar este método
de programación como otro elemento más de nuestros desarrollos. Para ello
debemos escoger preferiblemente herramientas de desarrollo que soporten dicho
modelo, e incluir en nuestros diseños los aspectos. Es probable que el
desarrollo de aplicaciones con aspectos comience a ser habitual a medida que
las herramientas lo soporten. Lo mismo ocurre con otros modelos de
programación, como por ejemplo la programación que incluya las pruebas (JUnit y
similares) o la programación en paralelo (stream programming).
14
Sobre el autor
Cristóbal González Almirón es licenciado
en CC Fisicas, aunque de profesión es desarrollador de aplicaciones.
Es experto en programación orientada a
objetos y C++.
Correo electrónico: cristobal_gonzalez en
yahoo com.