1. Programación por Aspectos. Una Introducción
Primer Borrador
Pablo Figueroa y Alejandro Isaza
9 de mayo de 2006
1. Introducción
A mediados de los 90 surgió la programación orientada a aspectos (POA), un nuevo paradigma
de programación que ha logrado un nivel de desarrollo interesante y que ha generado expectativas
importantes a nivel mundial. Este paradigma ofrece una nueva herramienta de modularización, el
aspecto, junto con nuevas formas de relacionar módulos, por medio de los designadores de enlace;
y mediante éstas herramientas provee formas novedosas de implementar aplicaciones que resultan
ser altamente modulares. Sus conceptos tienen similaridad con conceptos en otros paradigmas de
programación, como programación generativa [1], metaprogramación [4] o programación declarativa
[6], aunque sus seguidores han encontrado una forma de diferenciar y darle carácter propio al
desarrollo orientado por aspectos (DOA).
Este artículo es una introducción al tema de desarrollo orientado por aspectos. Este trabajo ha
surgido de grupos de discusión sobre el tema y del curso de pregrado que desarrollamos en el 2005
en la Universidad de los Andes, el cual está documentado en [2]. En esta introducción queremos
mostrar una breve reseña histórica sobre el tema, los conceptos fundamentales alrededor de la DOA,
definir sus características y proponer una forma de referirnos a sus conceptos en Castellano. Gracias
a la disponibilidad de ambientes de desarrollo como AspectJ [3] es posible para cualquier lector
con conocimientos en Java y en programación orientada por objetos desarrollar ejemplos de POA
y aplicar sus conceptos fundamentales. Al final presentamos referencias adicionales al tema y un
glosario de los conceptos que definimos, junto con los términos en inglés a los que corresponden
en la literatura actual de aspectos. Dichos términos serán destacados durante el texto en negrita.
Esperamos que esta introducción motive un estudio más profundo del tema por parte de los lectores
y la exploración de las posibilidades de esta tecnología en proyectos reales.
2. Historia
Desde los setentas, autores tan reconocidos como David Parnas han escrito sobre la importancia de
modularizar un programa de software [5], y de las formas en las cuales se puede desarrollar esta
1
2. labor. En resumen, un programa puede dividirse en términos de las estructuras de datos que lo
conforman, o de acuerdo a las tareas que soporta para sus usuarios (o requerimientos no funcionales
definidos por sus diseñadores). Generalmente no se puede lograr ambas descomposiciones al mismo
tiempo, lo cual genera problemas en el producto final, dependiendo del punto de vista desde el cual
se analice el software.
El término programación orientada por aspectos surgió a finales de 1995 en el grupo de trabajo
de Gregor Kiczales en Xerox Palo Alto Research Center (PARC), un autodidacta, empresario y
actualmente profesor de la Universidad de British Columbia. En esa época trabajaban en lo que
ellos llamaban Open Implementations, trabajo que produjo un conjunto de lenguajes de propósito
específico, de los cuales salió el concepto de Aspect Decomposition and Weaving (descomposición
aspectual y tejido), el cual se redujo posteriormente a programación orientada por aspectos. Algunas
ideas alrededor de este concepto fueron publicadas desde esta época, generalmente incompletas, lo
que dió lugar a un conjunto temprano de seguidores como también a varios detractores, los cuales
notaban claramente la debilidad e incompletitud de los conceptos.
A principios de 1998 fue liberada la primera versión (0.1) de AspectJ, como una alternativa para
POA genérica, a diferencia de los lenguajes de propósito específico que lo precedieron. Gracias al
soporte gubernamental que recibió Kiczales el lenguaje pudo seguir evolucionando desde este mo-
mento, convirtiendose en la prueba y ejemplo de los conceptos promovidos por POA. Hoy contamos
con la versión 1.5 de AspectJ, estable y madura, y con una comunidad de trabajo activa alrededor
del lenguaje.
Además de AspectJ, existen otras plataformas más o menos maduras para iniciarse en el tema de
aspectos. Composition Filters es un trabajo previo al de Kiczales, desarrollado por el grupo TRESE
en el departamento de Ciencias de Computación de la Universidad de Twente, en Holanda. Éste
ha sido adaptado a los conceptos de aspectos y es soportado sobre varios lenguajes tradicionales,
como Java, C++ y Smalltalk y .NET. HyperJ es un proyecto de IBM que soporta un modelo por
aspectos. Otras implementaciones son JBoss-AOP, Spring AOP, AspectC++, PHPAspect, AspectS
y AspectXML1 .
3. Aproximaciones a los Aspectos
En esta sección se incluyen varias formas complementarias de comprender el tema de programación
orientada a aspectos. Empezamos con un ejemplo y complementamos esta visión con una descripción
de los términos utilizados. Más adelante, comparamos el desarrollo por aspectos con el desarrollo
de proyectos en organizaciones matriciales y con los conceptos de programación por objetos.
3.1. Un ejemplo de programa
Un uso clásico de los conceptos en POA es la implementación de la funcionalidad de traza, la
cual busca dejar un registro de las actividades desarrolladas en la ejecución de la aplicación, con
1
Una lista más completa puede ser encontrada en http://www.aosd.net/wiki/index.php?title=Tools_for_Developers
2
3. propósitos de auditoría o de depuración. Supongamos que el programa consta de las clases de la
Figura 1.
Figura 1: Clases en programa de ejemplo
El programa principal hace uso de todos los servicios en las clases. Supongamos que deseamos
generar un registro de auditoría como el que se muestra en el siguiente listado:
Estoy en el metodo m1 de la clase ClaseA
Estoy en el metodo m4 de la clase ClaseD
...
Estoy en el metodo m5 de la clase ClaseE
Si queremos agregar esta funcionalidad siguiendo el paradigma orientado por objetos (OO), es
necesario tener acceso al código fuente del programa y cambiar el código en los métodos involucrados
de tal manera que se genere el mensaje deseado. En el mejor de los casos, se puede utilizar alguna
librería diseñada para generación de trazas, como java.util.logging o log4java. Un esquema de los
cambios generados al código se pueden observar en la Figura 2, en donde las líneas punteadas llevan
a un código de ejemplo que debe ser agregado en cada método.
Este método de agregar traza es complejo y dificil de mantener, por diversas razones: el código de
traza queda disperso por toda la aplicación, no se puede apagar dicha funcionalidad fácilmente,
y es muy fácil olvidar el código adicional en nuevos métodos. Se dice que el código resultante es
enmarañado, ya que cada método encapsula preocupaciones distintas: en este caso, el código
que ya estaba ejecutando y el nuevo código para generar la traza.
Un lenguaje orientado por aspectos permite hacer este tipo de cambios de una manera muy eficiente,
sin tocar el código del programa original. Adicionalmente, en lenguajes como AspectJ, se pueden
aplicar los cambios aún cuando no se tenga acceso al código fuente de las clases a las que se desea
agregar traza. Utilizando las capacidades de la POA, es posible crear dos cosas: un consejo que
encapsule el código que se quiere ejecutar en cada uno de los puntos donde se desee generar traza
y un designador de enlace, que identifique en qué lugares se debe agregar el código del consejo.
El código queda como se muestra en la Figura 3, en donde las líneas punteadas muestran en qué
lugares se agrega el código del consejo de una manera automática.
En este caso se puede observar las siguientes ventajas:
3
4. Figura 2: Cambios generados, paradigma OO
Figura 3: Cambios generados, paradigma OA
4
5. No hay que modificar el código ya existente, para agregar algo que no está directamente
relacionado con su funcionamiento actual.
No es necesario contar con el código fuente del programa.
Si se agregan nuevas clases o métodos al programa, sólo se debe cambiar el designador de
enlace para que dichos métodos también generen traza.
Sólo debe modificarse el consejo si se desea generar traza de una manera distinta. El código
relacionado con la traza queda concentrado en un solo lugar, lo cual cumple con la filosofiá de
modularidad básica.
Se puede eliminar fácilmente la funcionalidad de traza, eliminando el aspecto.
La ejecución del programa por aspectos es idéntica a la del programa por objetos, con la diferencia
que el trabajo del desarrollador es mucho más eficiente y no se tiene que replicar código en varias
partes del programa. El ambiente de programación por aspectos se encarga del proceso de tejido,
el cual agrega la funcionalidad de traza al resto del código, aliviando al programador de dicha tarea.
3.2. Conceptos fundamentales
En DOA, se supone que se puede dividir el software en preocupaciones, o asuntos que se desean
resolver y que son claramente distinguibles: el almacenamiento de datos, una tarea del usuario, el
tratamiento de seguridad, por ejemplo. El objetivo principal en DOA es expresar las distintas preo-
cupaciones de una manera independiente y modular, aumentando los mecanismos de modularización
existentes. Otro objetivo busca que se puedan agregar nuevos aspectos no previstos al código, lo
cual denominamos transparencia. Se denominan puntos de enlace a todos aquellos elementos en
un módulo que pueden relacionarse con otro. Algunos ejemplos de puntos de enlace son la entrada
o salida de un método, la consulta o modificación de un atributo, la inicialización de un objeto o la
generación de una excepción, aunque el conjunto de puntos varía dependiendo del lenguaje usado.
Una lenguaje orientado a aspectos permite modularizar el código en términos de preocupaciones.
Una preocupación en código se denomina un aspecto el cual define dónde se quiere agregar pedazos
de código, denominados consejos, mediante designadores de enlace. Un designador de enlace
permite generalmente cuantificar puntos de enlace por extensión, nombrando cada punto de enlace
deseado, o por comprensión, describiendo una expresión que puede designar varios puntos de enlace.
Hay ambientes de POA que consideran que todas las preocupaciones se aplican a un modulo domi-
nante, el cual se denomina la descomposición dominante. Hay otros que consideran este esquema
una tiranía, y consideran todas las preocupaciones al mismo nivel. Una vez se deciden los aspectos
que hacen parte de una aplicación se procede a ejecutar su tejido, el cual es el proceso de com-
poner el código de los distintos aspectos para poderlo ejecutar de una manera secuencial, como en
cualquier programa procedimental. El proceso de tejido es generalmente en tiempo de compilación,
aunque hay ambientes de desarrollo que hacen operaciones de tejido en ejecución. El proceso de
tejido genera un código enmarañado, el cual se esconde del programador por aspectos.
5
6. 3.3. Una interpretación organizacional
Una forma de comprender lo que agrega la POA a metodos tradicionales de programación como la
programación orientada por objetos es observando las caracterísiticas de estructuras organizacionales
matriciales. En dichas organizaciones, coexisten jerarquias por proyectos y por áreas de producción
o de servicio, como una manera de atender mejor a los clientes a la vez que se optimizan costos y
procedimientos. Los recursos se organizan dentro de la matriz proyectos versus áreas, y cada recurso
puede ser requerido en varios proyectos como en varias áreas. Un empleado de dicha organización
debe preocuparse por tareas en varios aspectos, bien sea relacionados a un proyecto o relacionados
a un área del negocio.
Los aspectos en software pueden asemejarse a recursos especializados en áreas del negocio, los cuales
pueden desempeñarse en varias aplicaciones, o quot;proyectosquot;. Estos recursos pueden utilizarse en
nuevos proyectos, y su conocimiento no depende de ningún proyecto en particular. Su intervención en
cada proyecto está dada por su flujo de trabajo, el cual determina en qué momento es indispensable
la intervención de dicho recurso. La posibilidad de tener recursos que atienden un mismo aspecto
en varios proyectos ha probado ser benéfica en este tipo de aplicaciones.
3.4. Una interpretación evolutiva desde objetos
El concepto de modularidad es fundamental en la programación orientada por objetos (POO),
implementado mediante el concepto de clase, como también lo son las formas de combinar dichos
módulos. La POO ofrece dos formas fundamentales de relación entre clases: delegación y herencia.
En delegación, una clase contiene objetos de otras clases, a los cuales puede delegar parte o la
totalidad de sus responsabilidades. En herencia, la definición de una clase B puede agregar atributos
o métodos a la definición de otra clase A, siendo posible además reemplazar instancias de A por
instancias de B.
La POA crea un nuevo concepto de módulo (el aspecto) y formas de relacionar estos nuevos módulos
con clases, mediante el concepto de puntos de enlace. En lenguajes como AspectJ se cuenta con una
gran variedad de puntos de enlace, que permiten agregar código antes y despues de métodos, tanto
en su llamado como en su ejecución, agregar atributos y métodos a una clase o agregar código a
ciertos métodos dentro del flujo de ejecución. Aunque existen implementaciones de ambientes para
POA que no son orientados por objetos, los que están implementados sobre ambientes de POO
permiten una gran variedad de formas para modularizar y componer código.
4. Comparación con otros paradigmas
Existen varias ideas en el área de desarrollo de software que dieron origen o que se relacionan con
el concepto de aspectos. Acá analizamos las siguientes: programación por objetos, programación
generativa, metalenguajes y programación declarativa.
Como ya describimos en la sección 3.4, la POA agrega nuevas formas de modularización y compo-
6
7. sición. Algunas de estas nuevas oportunidades son:
Agregar código a ciertos métodos. Es posible agregar consejos a uno o más métodos a la vez,
independientemente de las relaciones de herencia existentes entre las clases de dichos métodos.
Modificar una clase. Es posible agregar métodos y atributos a una clase sin crear nuevos tipos.
Esto permite modularizar responsabilidades disyuntas dentro de una clase, sin tener que crear
muchos tipos en el programa (este problema es generalmente denominado explosión de clases).
Agregar código y comportamiento sin tener que prever con anterioridad dónde, lo cual hace
más comprensible el código de la descomposición dominante.
La programación generativa busca, como su nombre lo indica, generar código desde descripciones
de alto nivel, concepto relacionado con el de lenguajes de dominio específico [1]. Existen muchos
puntos comunes entre programación generativa y aspectos. Es posible, por ejemplo, describir los
mecanismos de un lenguaje de aspectos en un lenguaje de dominio específico (el dominio de los
aspectos) y generar el código correspondiente en un lenguaje convencional (de hecho, hay varias
implementaciones de lenguajes de aspectos que funcionan de esta manera). Hay también varias
diferencias: algunos lenguajes de POA permiten hacer tejido en tiempo de ejecución, lo cual no
es posible en programación generativa. Es tambien importante en un lenguaje de POA definir un
conjunto claro de mecanismos de modularización y esconder los mecanismos de tejido, los cuales
son visibles en un ambiente generativo. En general, la programación generativa es un mecanismo
de implementación para ambientes de POA (no es único), y un ambiente de POA es mucho mas
específico en sus métodos de composición y modularización que un ambiente generativo.
Existe también una relación estrecha entre el paradigma de POA y los metalenguajes. Un lenguaje
de programación con caracterísiticas de metalenguaje permite escribir sentencias que se refieren al
código escrito, o en otras palabras a las mismas estructuras de programación o sentencias utiliza-
das. Existen muchos lenguajes de programación que tienen capacidades de metalenguajes, como por
ejemplo Smalltalk y Java, con sus capacidades de introspección. Es posible en general describir los
mecanismos de aspectos mediante metalenguajes, debido a la gran versatilidad de dichos ambientes.
Sin embargo, existen dos problemas con este esquema: lo enmarañado del codigo y las amplias posi-
bilidades existentes en los metalenguajes. Aunque se pueden implementar los mecanismos de POA
mediante un metalenguaje, en general el código resultante es complicado de escribir y compren-
der, por lo que un lenguaje de POA generalmente ofrece nueva sintaxis específica a sus propósitos.
Por otra parte, un metalenguaje puede hacer muchas otras cosas adicionales a las incluidas como
conceptos de POA, lo cual los hace ambientes de propósito general. POA puede verse como una
aplicación limitada de las capacidades de un metalenguaje, aunque con una sintaxis que le facilita
su utilización a un programador.
5. Referencias Adicionales
Por completar
7
8. 6. Conclusiones
De la misma manera como Smalltalk generó un gran interés por la programación orientada por obje-
tos a principios de los 70, AspectJ ha movido la comunidad internacional alrededor de los conceptos
de orientación por aspectos. La programación por aspectos ofrece un conjunto de herramientas adi-
cionales que permiten modularizar software mediante varios criterios, no únicamente los dominantes,
lo cual permite separar mejor las distintas facetas del software. El ambiente provisto por AspectJ
es lo suficientemente maduro para que programadores en Java lo incluyan en sus desarrollos. Cree-
mos que el uso de este tipo de herramientas pueden facilitar el desarrollo de aplicaciones, e influir
positivamente en la calidad del producto final.
7. Glosario
Aspecto (aspect). Un aspecto es la descripción en un lenguaje de programación de una preocu-
pación en el software.
Código enmarañado (code tangling). El resultado en código de tener preocupaciones disper-
sas.
Composición (composition). Es el proceso de unir componentes de alguna manera. Los lengua-
jes como AspectJ agregan nuevos métodos para crear composiciones sobre los existentes en
lenguajes orientados por objetos.
Consejo (advice). Cada uno de los “métodos” de un aspecto.
Cuantificación por extensión/comprensión (quantification). Un designador de enlace pue-
de nombrar puntos de enlace bien sea por extensión o comprensión. Por extensión significa
que deben nombrarse uno a uno todos los puntos de enlace. Por comprensión significa que
pueden utilizarse reglas o patrones para que el sistema busque todos los puntos de enlace que
cumplen con dicho patrón. Un ejemplo de regla en AspectJ es public * *.set*(..), la cual
va a incluir todos los métodos públicos que comiencen con set.
Descomposición dominante (dominant decomposition). En un programa orientado por ob-
jetos existe solo una descomposición. Una descomposición dominante en un lenguaje por as-
pectos corresponde generalmente al conjunto de módulos a los cuales se unen los aspectos.
Dichos módulos son importantes porque definen en cierta medida la estructura dominante del
programa.
Designador de enlace (point cut designator). Es la forma en la cual un aspecto se refiere a
los puntos de enlace en los cuales quiere agregar funcionalidad.
Envolver antes/despues (wrapping, before/after). Un método para agregar código alrededor
de un método.
Preocupación (concern). Es una característica del software. Puede corresponder a un requeri-
miento funcional o no funcional, o a alguna labor interna que debe desarrollar el software para
cumplir con su funcionalidad. Un ejemplo de preocupación es la persistencia de los datos en
el programa, o la autenticación de usuarios.
8
9. Preocupaciones dispersas (crosscutting concerns). Es la característica del software tradicio-
nal de tener mezclado el código que soluciona diversas preocupaciones.
Programación generativa (Generative programming). Transforma representaciones de alto
nivel de un programa a código de “bajo” nivel.
Puntos de enlace (join points). Son los lugares en los cuales se puede agregar funcionalidad,
correspondiente a preocupaciones no planeadas previamente. Cada lenguaje de programación
por aspectos define un conjunto de puntos de enlace, los cuales pueden ser estáticos (parte de
la sintaxis del programa) o dinámicos (parte del estado en ejecución del programa). Ejemplos
de puntos de enlace estáticos son cualquier método en una clase (paint() en un Applet, por
ejemplo). Ejemplos de puntos de enlace dinámicos son el cambio de una variable a un valor
en un rango, o la llamada de un método dentro de otro.
Tejido (weaving). Es el proceso que realiza el compilador (o ambiente de ejecución) de un lenguaje
por aspectos para agregar los consejos a los puntos de enlace correspondientes.
Transparencia (obliviousness). Se refiere al hecho de no identificar dónde se agrega el código
de los aspectos. El objetivo principal es poder agregar funcionalidad en el futuro sin tener
que planearla por anticipado. Por ejemplo, un mecanismo de listeners permite agregar código
a una clase, pero hay que planearlo por anticipado, y se nota en el código por los métodos
necesarios para agregar y eliminar listeners (por lo tanto no es transparente). Un mecanismo
transparente, como los usados en AspectJ, permiten agregar funcionalidad de una manera
transparente.
Traza (log, trace) . Registro de las actividades desarrolladas en la ejecución de la aplicación, con
propósitos de auditoría o de depuración.
Referencias
[1] Krzysztof Czarnecki and Ulrich W. Eisenecker. Generative Programming: Methods, Tools, and
Applications. Addison–Wesley, 2000.
[2] Pablo Figueroa. Teaching aspects to undergraduates. In AOSD 2006 Workshop. Aspects in
Teaching, 2006.
[3] AspectJ Group. The aspectj project at eclipse.org. http://www.eclipse.org/aspectj/, 2006.
[4] Gregor Kiczales, Jim des Rivières, and Daniel G. Bobrow. The Art of the Metaobject Protocol.
MIT Press, 1991.
[5] D. L. Parnas. On the criteria to be used in decomposing systems into modules. Commun. ACM,
15(12):1053–1058, 1972.
[6] Leon Sterling and Ehud Shapiro. The Art of Prolog. MIT Press, 1994.
9