scieee AI-readable full text Open interactive document viewer

Visión General de la Programación Orientada a Aspectos

Reina Quintero, Antonia María

Abstract

La ingeniería del software, y en general, la informática es una disciplina que está en constante evolución. Cada día surgen nuevas técnicas y metodologías que intentan mejorar la calidad y la eficiencia de los productos software. En los últimos tiempos ha surgido con fuerza una nueva forma de descomponer los sistemas: la orientación a aspectos. Este informe trata dar una visión general de la programación orientada a aspectos, desde sus comienzos, hasta llegar su estado actual. Se sentarán las bases sobre las que se apoya está tecnología, y se definirán los conceptos que maneja, sirviendo de presentación a los investigadores que quieran introducirse en este campo.

Full text

Visión General de la Programación Orientada a Aspectos Antonia Mª Reina Quintero Departamento de Lenguajes y Sistemas Informáticos Universidad de Sevilla Diciembre, 2000 Departamento de Lenguajes y Sistemas Informáticos Facultad de Informática y Estadística Universidad de Sevilla Avenida de la Reina Mercedes S/n. 41012 SEVILLA Fax/Tlf: 954557139 Visión General de la Programación Orientada a Aspectos 2 Índice 1. Objetivos del documento...................................................................................................3 2. Introducción.......................................................................................................................4 3. Un poco de historia............................................................................................................9 4. ¿Qué es un aspecto?.........................................................................................................10 5. Fundamentos de la programación orientada a aspectos...................................................13 6. Tejiendo clases y aspectos...............................................................................................15 7. Estado del Arte en el Diseño de Lenguajes de Aspectos.................................................18 Lenguajes de Aspectos de Propósito General vs. Dominio Específico...............................18 Un lenguaje de dominio específico: COOL.........................................................................19 Un lenguaje de propósito general: AspectJ .........................................................................21 Un ejemplo: La gestión de una cola circular ......................................................................24 Una cola circular usando COOL..........................................................................................25 Una cola circular usando AspectJ........................................................................................29 El problema de los lenguajes base.......................................................................................31 8. Los aspectos en el diseño.................................................................................................33 9. Disciplinas relacionadas con POA...................................................................................38 Reflexión y protocolos de metaobjetos................................................................................38 Transformación de programas.............................................................................................38 Programación subjetiva .......................................................................................................38 10. Conclusiones................................................................................................................39 11. Listado de Figuras .......................................................................................................40 12. Referencias ..................................................................................................................41 Visión General de la Programación Orientada a Aspectos 3 1. Objetivos del documento La ingeniería del software, y en general, la informática es una disciplina que está en constante evolución. Cada día surgen nuevas técnicas y metodologías que intentan mejorar la calidad y la eficiencia de los productos software. En los últimos tiempos ha surgido con fuerza una nueva forma de descomponer los sistemas: la orientación a aspectos. Este informe trata dar una visión general de la programación orientada a aspectos, desde sus comienzos, hasta llegar su estado actual. Se sentarán las bases sobre las que se apoya está tecnología, y se definirán los conceptos que maneja, sirviendo de presentación a los investigadores que quieran introducirse en este campo. Visión General de la Programación Orientada a Aspectos 4 2. Introducción Si se echa un vistazo a la historia de la ingeniería del software, prestando particular atención a cómo ha evolucionado, se puede observar que los progresos más significativos se han obtenido gracias a la aplicación de uno de los principios fundamentales a la hora de resolver cualquier problema, incluso de la vida cotidiana, la descomposición de un sistema complejo en partes que sean más fáciles de manejar, es decir, gracias a la aplicación del dicho popular conocido como ’divide y vencerás’. En los primeros estadios de desarrollo de los lenguajes de programación se tenía un código en el que no había separación de conceptos, datos y funcionalidad se mezclaban sin una línea divisoria clara. A esta etapa se la conoce como la del código spaghetti, ya que se tenía una maraña entre datos y funcionalidad que recuerda a la que se forma cuando comemos un plato de esta pasta italiana. En la siguiente etapa se pasó a aplicar la llamada descomposición funcional, que pone en práctica el principio de ‘divide y vencerás’ identificando las partes más manejables como funciones que se definen en el dominio del problema. La principal ventaja que proporciona esta descomposición es la facilidad de integración de nuevas funciones, aunque también tiene grandes inconvenientes, como son el hecho de que las funciones quedan algunas veces poco claras debido a la utilización de datos compartidos, y el que los datos quedan esparcidos por todo el código, con lo cual, normalmente el integrar un nuevo tipo de datos implica que se tengan que modificar varias funciones. Intentando solventar estas desventajas con respecto a los datos, se dio otro paso en el desarrollo de los sistemas software. La programación orientada a objetos (POO) ha supuesto uno de los avances más importantes de los últimos años en la ingeniería del software para construir sistemas complejos utilizando el principio de descomposición, ya que el modelo de objetos subyacente se ajusta mejor a los problemas del dominio real que la descomposición funcional. La ventaja que tiene es que es fácil la integración de nuevos datos, aunque también quedan las funciones esparcidas por todo el código, y tiene los inconvenientes de que, con frecuencia, para realizar la integración de nuevas funciones hay que modificar varios objetos, y de que se produce un enmarañamiento de los objetos en funciones de alto nivel que involucran a varias clases. En la Figura 1 se representa mediante un esquema los distintos estadios en la evolución de los sistemas software. En este esquema se refleja de forma clara la mezcla de conceptos que se produce en cada una de las etapas de la evolución (etapas comúnmente conocidas como “generaciones”). Si cada una de las distintas formas que aparecen dibujadas (triángulo, cuadrado, trapecio, elipse) representa a un tipo de datos distinto, y cada color o tonalidad representa a una función distinta, se tiene que en la primera generación de los sistemas software funciones y datos se entremezclaban. Visión General de la Programación Orientada a Aspectos 5 Figura 1 Esquema de la evolución de la ingeniería del software En la segunda y tercera generación se tienen conjuntos de elementos agrupados por tonalidades, y en cada conjunto conviven distintas formas, lo que nos indica que la descomposición del sistema se hace en base a las funciones (tonalidades), y que los datos quedan esparcidos por todos los conjuntos. En la cuarta generación, sin embargo, esta agrupación se realiza en base a las formas (datos), pero como se puede observar, en cada conjunto tenemos representadas distintas funcionalidades (tonalidades), con lo cual éstas también se nos dispersan por todo el sistema. Uno de los principales inconvenientes con el que nos encontramos al aplicar estas descomposiciones ya tradicionales es que muchas veces se tienen ejecuciones ineficientes debido a que las unidades de descomposición no siempre van acompañadas de un buen tratamiento de aspectos tales como la gestión de memoria, la coordinación, la distribución, las restricciones de tiempo real, ... En el desarrollo de un sistema software además del diseño y la implementación de la funcionalidad básica, se recogen otros aspectos tales como la sincronización, la distribución, el manejo de errores, la optimización de la memoria, la gestión de Sofware = Datos (formas) + Funciones (colores) 1ª Generación: Código espaghetti 4ª Generación: Descomposición en objetos 2ª y 3ª Generación: Descomposición funcional Visión General de la Programación Orientada a Aspectos 6 seguridad, etc. Mientras que las descomposiciones funcional y orientada a objetos no nos plantean ningún problema con respecto al diseño y la implementación de la funcionalidad básica, estas técnicas no se comportan bien con los otros aspectos. Es decir, que nos encontramos con problemas de programación en los cuales ni las técnicas funcionales, ni las orientadas a objeto son suficientes para capturar todas las decisiones de diseño que el programa debe implementar. Con las descomposiciones tradicionales no se aislan bien estos otros aspectos, sino que quedan diseminados por todo el sistema enmarañando el código que implementa la funcionalidad básica, y yendo en contra de la claridad del mismo. Se puede afirmar entonces que las técnicas tradicionales no soportan bien la separación de competencias para aspectos distintos de la funcionalidad básica de un sistema, y que esta situación claramente tiene un impacto negativo en la calidad del software. La programación orientada a aspectos ( POA) es una nueva metodología de programación que aspira a soportar la separación de competencias para los aspectos antes mencionados. Es decir, que intenta separar los componentes y los aspectos unos de otros, proporcionando mecanismos que hagan posible abstraerlos y componerlos para formar todo el sistema. En definitiva, lo que se persigue es implementar una aplicación de forma eficiente y fácil de entender. POA es un desarrollo que sigue al paradigma de la orientación a objetos, y como tal, soporta la descomposición orientada a objetos, además de la procedimental y la descomposición funcional. Pero, a pesar de esto, POA no se puede considerar como una extensión de la POO, ya que puede utilizarse con los diferentes estilos de programación ya mencionados. El estado actual de la investigación en POA es análogo al que había hace veinte años en la programación orientada a objetos. En la Figura 2 se representa la forma en que la programación orientada a aspectos descompone los sistemas. Se sigue el razonamiento empleado en la Figura 1, donde los datos se identificaban con la forma de las figuras y las funcionalidades con el color o la tonalidad. En este esquema se observa que la disociación de los de los distintos conjuntos se realiza tanto en base a la forma (datos) como a las tonalidades (funciones). Además, se indica que las distintas funcionalidades están relacionadas de alguna manera. Esto se representa utilizando figuras transparentes para indicar este tipo de relación. Por último, vista la evolución de la ingeniería del software, y comprobando que las formas de descomponer los sistemas han conducido a nuevas generaciones de sistemas, cabe plantearnos la siguiente pregunta ¿estaremos en la etapa incipiente de una nueva generación de sistemas software? Visión General de la Programación Orientada a Aspectos 7 Figura 2. Descomposición en aspectos ¿Una quinta generación? Este trabajo se distribuye de la siguiente manera: En el tercer apartado se explica brevemente cómo surge la idea de la orientación a aspectos, y aunque nos encontramos con una tecnología bastante joven, se ha creído conveniente dar una breve introducción a sus orígenes para resolver una de las preguntas básicas y más repetidas a lo largo de la humanidad. ¿De dónde venimos?. En el resto de los apartados se responderá a otra de las preguntas pilares: ¿Qué somos?. Algunas veces, el responder a qué somos nos puede ayudar a conocer hacia dónde vamos, pero esta última pregunta ayudarán a responderla los investigadores que decidan estudiar disciplina, y algunos de ellos, ayudarán a elegir un camino para decidir hacia dónde vamos. En el cuarto apartado se intenta dar una definición más o menos formal, más o menos intuitiva de el concepto básico que maneja la POA, el aspecto. El quinto intenta definir algunos conceptos nuevos que se introducen al aplicar esta nueva tecnología, así como sentar las bases para que cualquier investigador se pueda poner a trabajar. Una parte importante de la programación orientada a aspectos es que las clases y los aspectos se han de mezclar. Existen dos enfoques principalmente para resolver el modo de realizar este entrelazado. Éstos se comentan en el apartado seis. Si no existen herramientas, no se puede trabajar, y los lenguajes orientados a aspectos son una herramienta importante a la hora de trabajar con aspectos. Aquí también hay distintas perspectivas a la hora de diseñar estos lenguajes. Esta problemática se aborda en el apartado siete, donde se comenta someramente la estructuras de dos lenguajes de aspectos, uno de dominio específico (COOL), y otro de propósito general (AspectJ). En este mismo apartado se incluye un ejemplo de implementación. Se escribe el código de una aplicación para gestionar una cola circular con un aspecto de ¿5ª Generación? : Descom p osición de as p ectos Visión General de la Programación Orientada a Aspectos 8 sincronización, utilizando los lenguajes tradicionales (Java), uno de aspectos de dominio específico (COOL), y uno de aspectos de propósito general (AspectJ). Así se podrán comparar los tres enfoques y ver las ventajas y los inconvenientes de cada uno. Los aspectos no se han de quedar sólo en la implementación, sino que se han de llevar hasta la fase de diseño, y en el apartado ocho se comenta la propuesta de Yamamoto, que se basa en la extensión de UML para dar este paso. En el apartado nueve, se comentan algunas de las disciplinas que han tenido una relación más estrecha con la orientación a aspectos. Y, por último, en el apartado diez se obtienen algunas conclusiones obtenidas durante la realización de este informe. Visión General de la Programación Orientada a Aspectos 9 3. Un poco de historia El concepto de programación orientada a aspectos fue introducido por Gregor Kiczales y su grupo, aunque el equipo Demeter había estado utilizando ideas orientadas a aspectos antes incluso de que se acuñara el término. El trabajo del grupo Demeter [7] estaba centrado en la programación adaptativa, que no es más que una instancia temprana de la programación orientada a aspectos. La programación adaptativa se introdujo alrededor de 1991. Aquí los programas se dividían en varios bloques de cortes. Inicialmente, se separaban la representación de los objetos del sistema de cortes. Luego se añadieron comportamientos de estructuras y estructuras de clases como bloques constructores de cortes. Cristina Lopes [2] propuso la sincronización y la invocación remota como nuevos bloques. No fue hasta 1995 cuando se publicó la primera definición temprana del concepto de aspecto [7], realizada también por el grupo Demeter, a la que se hace referencia en el apartado siguiente. Gracias a la colaboración de Cristina Lopes y Karl J. Lieberherr con Gregor Kiczales y su grupo se introdujo el término de programación orientada a aspectos Entre los objetivos que se ha propuesto la programación orientada a aspectos están principalmente el de separar conceptos y el de minimizar las dependencias entre ellos. Con el primer objetivo se consigue que cada cosa esté en su sitio, es decir, que cada decisión se tome en un lugar concreto, con el segundo se tiene una pérdida del acoplamiento entre los distintos elementos. De la consecución de estos objetivos se pueden obtener las siguientes ventajas: • Un código menos enmarañado, más natural y más reducido. • Una mayor facilidad para razonar sobre las materias, ya que están separadas y tienen una dependencia mínima. • Más facilidad para depurar y hacer modificaciones en el código. • Se consigue que un conjunto grande de modificaciones en la definición de una materia tenga un impacto mínimo en las otras. • Se tiene un código más reusable y que se puede acoplar y desacoplar cuando sea necesario. Visión General de la Programación Orientada a Aspectos 16 correspondiente método de la clase del objeto y ejecutará la implementación que tenga. Para hacer que el código tejido tenga efecto, el tejedor cambia la clase de todos los objetos Rectángulo a la clase tejida. También instala un mecanismo para que esto ocurra con todos los objetos rectángulo que se añadan en el futuro. Este tejedor también tiene en cuenta el orden en el que se entremezclan los aspectos. Esto lo resuelve asignando una prioridad al aspecto. El aspecto que tenga asignado un número menor es el que se teje primero, y por lo tanto, aparecerá antes en la jerarquía de herencia. Esta prioridad se ha representado en la estructura UML de las clases como un número entre paréntesis después de la clase estereotipada. Figura 7. Estructura de clases resultado de tejer dos aspectos y una clase con el Tejedor AOP/ST. El principal inconveniente subyacente bajo este enfoque es el rendimiento y que se utiliza más memoria con la generación de todas estas subclases. Una de las primeras clasificaciones de las formas de combinar el comportamiento de los componentes y los aspectos fue dada por John Lamping [6]: Rectángulo area extensión <<capa de métodos tejidos con traza (50)>> area <<capa de métodos tejidos con sincronización (100)>> area extensión <<clase tejida>> Rectángulo Visión General de la Programación Orientada a Aspectos 17 1. Yuxtaposición. Consiste en la intercalación del código de los aspectos en el de los componentes. La estructura del código mezclado quedaría como el código base con el código de los aspectos añadidos en los puntos de enlace. En este caso, el tejedor sería bastante simple. 2. Mezcla. Es lo opuesto a la yuxtaposición, todo el código queda mezclado con una combinación de descripciones de componentes y aspectos. 3. Fusión. En este caso, los puntos de enlace no se tratan de manera independiente, se fusionan varios niveles de componentes y de descripciones de aspectos en una acción simple. Visión General de la Programación Orientada a Aspectos 18 7. Estado del Arte en el Diseño de Lenguajes de Aspectos En este apartado se comentan las distintas tendencias que se siguen en los lenguajes de aspectos. Hasta ahora se han distinguido dos enfoques diferentes en el diseño de los lenguajes de aspectos: los lenguajes de aspectos de dominio específico y los lenguajes de aspectos de propósito general. Lenguajes de Aspectos de Propósito General vs. Dominio Específico. Los lenguajes de aspectos de dominio específico soportan uno o más de estos sistemas de aspectos que se han ido mencionando en las secciones anteriores (distribución, coordinación, manejo de errores, ...), pero no pueden soportar otros aspectos distintos de aquellos para los que fueron diseñados. Los lenguajes de aspectos de dominio específico normalmente tienen un nivel de abstracción mayor que el lenguaje base y, por tanto, expresan los conceptos del dominio específico del aspecto en un nivel de representación más alto. Estos lenguajes normalmente imponen restricciones en la utilización del lenguaje base. Esto se hace para garantizar que los conceptos del dominio del aspecto se programen utilizando el lenguaje diseñado para este fin y evitar así interferencias entre ambos. Se quiere evitar que los aspectos se programen en ambos lenguajes lo cual podría conducir a un conflicto. Como ejemplos de lenguajes de dominio específico están COOL [2], que trata el aspecto de sincronización, y RIDL [2], para el aspecto de distribución. Los lenguajes de aspectos de propósito general se diseñaron para ser utilizados con cualquier clase de aspecto, no solamente con aspectos específicos. Por lo tanto, no pueden imponer restricciones en el lenguaje base. Principalmente soportan la definición separada de los aspectos proporcionando unidades de aspectos. Normalmente tienen el mismo nivel de abstracción que el lenguaje base y también el mismo conjunto de instrucciones, ya que debería ser posible expresar cualquier código en las unidades de aspectos. Un ejemplo de este tipo de lenguajes es AspectJ, que utiliza Java como base, y las instrucciones de los aspectos también se escriben en Java. Si contrastamos estos dos enfoques, propósito general versus propósito específico, se tiene que los lenguajes de aspectos de propósito general no pueden cubrir completamente las necesidades. Tienen un severo inconveniente: Permiten la separación del código, pero no garantizan la separación de funcionalidades, es decir, que la unidad de aspecto solamente se utiliza para programar el aspecto. Sin embargo, esta es la idea central de la programación orientada a aspectos. En comparación con los lenguajes de aspectos de propósito general, los lenguajes de aspectos de dominio específico fuerzan la separación de funcionalidades. Visión General de la Programación Orientada a Aspectos 19 Si hacemos esta comparación desde el punto de vista empresarial, siempre les será más fácil a los programadores el aprender un lenguaje de propósito general, que el tener que estudiar varios lenguajes distintos de propósito específico, uno para tratar cada uno de los aspectos del sistema. Un lenguaje de dominio específico: COOL COOL es un lenguaje de dominio específico creado por Xerox [2] cuya finalidad es la sincronización de hilos concurrentes. El lenguaje base que utiliza es una versión restringida de Java, ya que se han de eliminar los métodos wait, notify y notifyAll, y la palabra clave synchronized para evitar que se produzcan situaciones de duplicidad al intentar sincronizar los hilos en el aspecto y en la clase. En COOL, la sincronización de los hilos se especifica de forma declarativa y, por lo tanto, más abstracta que la correspondiente codificación en Java. COOL proporciona mecanismos para trabajar con la exclusión mutua de hilos de ejecución, el estado de sincronización, la suspensión con guardas, y la notificación de forma separada de las clases. Un programa COOL está formado por un conjunto de módulos coordinadores. En cada coordinador se define una estrategia de sincronización, en la cual pueden intervenir varias clases. Aunque estén asociados con las clases, los coordinadores no son clases. La unidad mínima de sincronización que se define en COOL es un método. Los coordinadores no se pueden instanciar directamente, sino que se asocian con las instancias de las clases a las que coordinan en tiempo de instanciación. Esta relación está vigente durante toda la vida de los objetos y tiene un protocolo perfectamente definido [2]. Figura 8 Protocolo entre un objeto y su coordinador en COOL. 1 m(){...} 2 3 4 5 6 7 8 Coordinador Ob j eto 1: En el hilo H se invoca al método m del objeto (obj.m()) 2: La petición se envía al coordinador del objeto. 3: El coordinador comprueba las restricciones de exclusión y las precondiciones para el método m. Si se da alguna restricción o no se cumple una precondición, se suspende H. Cuando todo se cumpla, T puede ejecutar el método m. Justo antes de esto, el coordinador ejecuta el bloque on_entry asociado con el método m. 4: Se envía la petición al objeto. 5: El hilo H ejecuta el método m en el objeto. 6: Se le envía el retorno del método al coordinador. 7: El coordinador ejecuta su bloque on_exit para el método m. 8: Finalmente se retorna de la invocación de m. Visión General de la Programación Orientada a Aspectos 20 Los coordinadores se escriben sabiendo perfectamente las clases a las que coordinan, sin embargo, las clases no tienen conocimiento de los coordinadores. Para tener una idea general de cómo trabaja el coordinador COOL, qué forma tiene un coordinador. El cuerpo de un coordinador puede esta formado por : Variables de condición. Las variables de condición se utilizan para controlar el estado de sincronización, con el propósito de utilizarlas en la suspensión con guarda y en la notificación de los hilos de ejecución. Se declaran utilizando la palabra clave condition. Variables ordinarias. Las variables ordinarias mantienen la parte del estado del coordinador que no conduce directamente a la suspensión con guarda y a la notificación de hilos, pero que pueden afectar al estado de sincronización. Se declaran igual que en Java. Un conjunto de métodos autoexcluyentes. En esta sección se identifican los métodos que solamente pueden ser ejecutados por un hilo a la vez. Se identifican con la palabra clave selfex. Varios conjuntos de métodos de exclusión mutua. Un conjunto de exclusión mutua identifica una serie de métodos que no se pueden ejecutar concurrentemente por distintos hilos. Es decir, que la ejecución de un hilo H de uno de los métodos del conjunto evita que otro hilo H’ ejecute cualquier otro método del mismo conjunto. El conjunto de exclusión mutua se declara con mutex. Gestores de métodos. Estos gestores se encargan de la suspensión con guarda y de la notificación de los hilos utilizando al estilo de las precondiciones utilizando la cláusula requires (a modo de precondición) y las sentencias on_entry y on_exit. La semántica de la suspensión con guarda es la siguiente: Cuando un hilo H quiere ejecutar un método M que tiene una precondición, definida en una cláusula requires, y se cumplen las restricciones de exclusión puede suceder lo siguiente: Visión General de la Programación Orientada a Aspectos 21 1. Si la condición definida en requires se cumple, entonces el método M puede ser ejecutado por H. 2. Si no el hilo H no puede ejecutar M, y se suspende. El hilo permanecerá suspendido hasta que se cumpla la precondición. Cuando esto ocurra, se le notificará a H, y si la restricción de exclusión aún se mantiene, H podrá ejecutar M, pero si no se mantiene, se volverá a suspender H. Las sentencias on_entry y on_exit se ejecutan cuando un hilo tiene permiso para ejecutar un método. Justo antes de ejecutarlo, se ejecutan las sentencias incluidas en el bloque on_entry, y justo después las del bloque on_exit. Un lenguaje de propósito general: AspectJ AspectJ es una extensión a JavaTM orientada a aspectos y de propósito general [14]. AspectJ extiende Java con una nueva clase de módulos llamado aspecto. Los aspectos cortan las clases, las interfaces y a otros aspectos. Los aspectos mejoran la separación de competencias haciendo posible localizar de forma limpia los conceptos de diseño del corte. En AspectJ, un aspecto es una clase, exactamente igual que las clases Java, pero con una particularidad, que pueden contener unos constructores de corte, que no existen en Java. Los cortes de AspectJ capturan colecciones de eventos en la ejecución de un programa. Estos eventos pueden ser invocaciones de métodos, invocaciones de constructores, y excepciones de señales y gestión. Los cortes no definen acciones, sino que describen eventos. En defnitiva, en AspectJ los aspectos son constructores que trabajan al cortar de forma transversal la modularidad de las clases de forma limpia y cuidadosamente diseñada [18]. Por lo tanto, un aspecto puede afectar a la implementación de un número de métodos en un número de clases, lo que permite capturar la estructura de corte modular de este tipo de conceptos de forma limpia. En general, un aspecto en AspectJ está formado por una serie de elementos: Cortes. Los cortes (pointcut) capturan colecciones de eventos en la ejecución de un programa. Estos eventos pueden ser invocaciones de métodos, invocaciones de constructores, y señalización y gestión de excepciones. Los cortes no definen acciones, simplemente describen eventos. Un corte está formado por una parte izquierda y una parte derecha, separadas ambas por dos puntos. En la parte izquierda se define el nombre del corte y el contexto del corte. La parte derecha define los eventos del corte. Visión General de la Programación Orientada a Aspectos 22 Los cortes se utilizan para definir el código de los aspectos utilizando avisos. A los descriptores de eventos de la parte derecha de la definición del corte se les llama designadores. Un designador puede ser: o Un método. o Un constructor. o Un manejador de excepciones. Se pueden componer utilizando los operadores o (“|”), y (“&”) y no (“!”). También se pueden utilizar caracteres comodines en la descripción de los eventos. Introducciones. Las introducciones (introduction) se utilizan para introducir elementos completamente nuevos en las clases dadas. Entre estos elementos podemos añadir: o Un nuevo método a la clase. o Un nuevo constructor. o Un atributo. o Varios de los elementos anteriores a la vez. o Varios de los elementos anteriores en varias clases. Avisos. Las declaraciones de avisos (advice) definen partes de la implementación del aspecto que se ejecutan en puntos bien definidos. Estos puntos pueden venir dados bien por cortes con nombre, bien por cortes anónimos. En las dos siguientes figuras se puede ver el mismo aviso, primero definido mediante un corte con nombre, y después mediante uno anónimo. Figura 9. Aviso definido mediante un corte con nombre. Figura 10. Aviso definido mediante un corte anónimo. pointcut emisores (Point p1, int newval): p1 & (void setX (newval)|void setY(newval); advice (Point p1, int newval): emisores (p1, newval){ before {System.out.println (“P1:“ + p1); } advice (Point p1, int newval): p1 & (void setX (newval) | void setY(newval) { before {System.out.println (“P1:“ + p1); } Visión General de la Programación Orientada a Aspectos 23 El cuerpo de un aviso puede añadir en distintos puntos del código, cada uno de los cuales se define mediante una palabra clave: o Aviso before.- Se ejecuta justo antes de que lo hagan las acciones asociadas con los eventos del corte. o Aviso after.- Se ejecuta justo después de que lo hayan hecho las acciones asociadas con los eventos del corte. o Aviso catch.- Se ejecuta cuando durante la ejecución de las acciones asociadas con los eventos definidos en el corte se ha elevado una excepción del tipo definido en la propia cláusula catch. o Aviso finally.- Se ejecuta justo después de la ejecución de las acciones asociadas con los eventos del corte, incluso aunque se haya producido una excepción durante la misma. o Aviso around.- Atrapan la ejecución de los métodos designados por el evento. La acción original asociada con el mismo se puede invocar utilizando thisJoinPoint.runNext(). Los avisos se pueden declarar con el modificador static, indicado esto que se ejecuta el mismo aviso para todas las instancias de las clases designadas. Si se omite indica que solamente se ejecuta para ciertas instancias. Una forma de tener más control sobre cómo afecta la declaración de los avisos al comportamiento de los objetos individuales es con las instancias de los aspectos. Para obtener una instancia de un aspecto se trabaja de la misma forma que para tener una instancia de una clase en Java. Lo más interesante de esto es componer instancias de aspectos con instancias de clases normales, donde juegan un papel muy importante los cortes. Esta composición se puede realizar de dos formas: 1. Con una composición explícita que es soportada por las acciones de corte estáticas. Aquí todas las partes de estado y comportamiento se capturan por los objetos normales. Los aspectos pegan estas partes separadas. 2. Con un mecanismo de composición automático soportado por las acciones de corte no estáticas. Este mecanismo tiene una naturaleza más dinámica que el estático. Se ejecuta para los aspectos que tengan al objeto invocado en su dominio. Los objetos se insertan en el dominio de los aspectos utilizando el método addObject disponible para todos los aspectos. Visión General de la Programación Orientada a Aspectos 24 Un ejemplo: La gestión de una cola circular Una vez que se han dado pinceladas de los dos enfoques diferentes existentes a la hora de diseñar los lenguajes de aspectos, se verá con un ejemplo práctico de cómo se reflejan estas características en el código de las aplicaciones. En primer lugar, se implementará la cola circular en Java sin el aspecto de sincronización. Luego, se le añadirán las líneas de código necesarias para gestionar este aspecto, y se podrá apreciar claramente cómo estas se diseminan por todo el programa, quedando un código poco claro. Después el mismo ejemplo se implementará con COOL, y por último con Aspect J. En la Figura 11, se muestra el código Java de una lista circular, en principio, sin restricciones de sincronización, adaptado de [8]. La clase ColaCircular se representa con un array de elementos. Los elementos se van añadiendo en la posición ptrCola (por atrás), y se van borrando en la posición apuntada por ptrCabeza (la parte de delante). Estos dos índices se ponen a cero al alcanzar la capacidad máxima del array. Esta inicialización se consigue utilizando el resto obtenido al dividir la posición a tratar entre el número total de elementos del array. Esta clase solamente contiene un constructor y dos métodos: Insertar para introducir elementos en la cola, y Extraer, para sacarlos. El problema del código de la Figura 11 es que si varios hilos solicitan ejecutar métodos del objeto ColaCircular, éstos no están sincronizados, pudiéndose dar el caso de que la cola haya cubierto su capacidad, y se sigan haciendo peticiones de inserción de elementos, y al contrario, es decir, que la cola esté vacía y se hagan peticiones de extracción de elementos. En estos casos, el hilo que hace la solicitud debería esperar a que se extraiga algún elemento, en el primer caso, o a que se inserte alguno en el segundo. Es decir, que se necesita código adicional para eliminar estas inconsistencias. Java permite coordinar las acciones de hilos de ejecución utilizando métodos e instrucciones sincronizadas. A los objetos a los que se debe coordinar el acceso se le incluyen métodos sincronizados. Estos métodos se declaran con la palabra reservada synchronized. Un objeto solamente puede invocar a un método sincronizado al mismo tiempo, lo que impide que los métodos sincronizados en hilos de ejecución entren en conflicto. Todas las clases y objetos tienen asociados un monitor, que se utiliza para controlar la forma en que se permite que los métodos sincronizados accedan a la clase u objeto. Cuando se invoca un método sincronizado para un objeto determinado, no puede invocarse ningún otro método de forma automática. Un monitor se libera automáticamente cuando el método finaliza su ejecución y regresa. También se puede liberar cuando un método sincronizado ejecuta ciertos métodos, como wait(). El subproceso asociado con el método sincronizado se convierte en no ejecutable hasta que se satisface la situación de espera y ningún otro método ha adquirido el monitor del objeto. Los métodos notify () se emplean para notificar a los hilos de ejecución en Visión General de la Programación Orientada a Aspectos 25 espera que el tiempo de espera se ha acabado. En Java, por defecto, no se define ninguna estrategia de sincronización. Una cola circular usando COOL Figura 11 Código de la clase cola circular sin restricciones de sincronización. La versión sincronizada del ejemplo de la Figura 11 se muestra en la Figura 12 . Aquí se ha introducido la exclusión mutua y condiciones con guardas. El código que se introduce para la sincronización es el sombreado. public class ColaCircular { private Object[] array; private int ptrCola = 0, ptrCabeza = 0; private int eltosRellenos = 0; public ColaCircular (int capacidad) { array = new Object [capacidad]; } public void Insertar (Object o) { array[ptrCola] = o; ptrCola = (ptrCola + 1) % array.length; eltosRellenos++; } public Object Extraer () { Object obj = array[ptrCabeza]; array[ptrCabeza] = null; ptrCabeza = (ptrCabeza + 1) % array.length; eltosRellenos--; return obj; } } Visión General de la Programación Orientada a Aspectos 32 Con respecto a los lenguajes de dominio específico, puede tener bastante importancia el hecho de escoger un lenguaje base. Se tiene que tener en cuenta que los puntos de enlace solamente pueden ser los que se identifiquen en el lenguaje base. Así que no se es completamente libre para diseñar los puntos de enlace. Segundo, si necesitamos separar las funcionalidades, debemos recordar que el lenguaje base debe restringirse después de que se hayan separado los aspectos. Esta es la parte más difícil, ya que tenemos que quitar elementos de un sistema complejo, el lenguaje base. Aunque el diseño de un lenguaje de programación es una tarea difícil y compleja, aún lo es más el hacerle cambios a un lenguaje, que no fue diseñado para tal propósito. Por ejemplo, las restricciones no deben ser tan fuertes como en el caso de COOL. Los lenguajes de aspectos de propósito general son menos difíciles de implementar por encima de un lenguaje de programación existente, ya que no necesitan restringir el lenguaje base. Aparte de esto, la situación es la misma que con los lenguajes de dominio específicos, es decir, en el diseño de los puntos de enlace, uno se limita a los que pueden definirse en el lenguaje base. Visión General de la Programación Orientada a Aspectos 33 8. Los aspectos en el diseño En las primeras fases, la orientación a aspectos se centró principalmente en el nivel de implementación y codificación, pero en los últimos tiempos cada vez se elevan más voces y surgen más trabajos para llevar la separación de funcionalidades a nivel de diseño. Esta separación se hace necesaria, sobre todo, cuando las aplicaciones necesitan un alto grado adaptabilidad o que se puedan reutilizar. Los trabajos surgidos hasta el momento proponen utilizar UML como lenguaje de modelado, ampliando su semántica con los mecanismos que el propio lenguaje unificado tiene para tales efectos y consiguiendo así representar el diseño funcional de los objetos separado del diseño no funcional del mismo, o lo que es lo mismo, representar la funcionalidad básica separada de los otros aspectos. Las ventajas que tiene el capturar los aspectos ya desde la fase de diseño son claras [19]: Facilita la creación de documentación y el aprendizaje. El tener los aspectos como constructores de diseño permite que los desarrolladores los reconozcan en los primeros estadios del proceso de desarrollo, teniendo así una visión de más alto nivel y ayudando así a que los diseñadores de aspectos y los principiantes puedan aprender y documentar los modelos de aspectos de un modo más intuitivo, pudiendo incluso utilizar herramientas CASE para tener representado el modelado de forma visual. Facilita la reutilización de los aspectos. La facilidad de documentación y aprendizaje influye en la reutilización de la información de los aspectos. Al saber cómo se diseña y cómo afecta a otras clase, es más fácil ver cómo se pueden utilizar de otra forma, lo que incrementaría la reutilización de los aspectos. La propuesta que se realiza en [19] de extensión del metamodelo de UML para tener en cuenta a los aspectos en la fase de diseño consiste en los siguiente: Añadir nuevos elementos al metamodelo para el aspecto y la clase “tejida”, y se reutilizar un elemento ya existente para la relación clase-aspecto. Aspecto. El aspecto se añade como un nuevo constructor derivado del elemento Clasificador (Figura 17) que describe características estructurales y de comportamiento [20] . Visión General de la Programación Orientada a Aspectos 34 Figura 17. El Aspecto como un elemento del metamodelo UML derivado de Clasificador Un aspecto así modelado puede tener atributos, operaciones y relaciones. Los atributos son utilizados por su conjunto de definiciones tejidas. Las operaciones se consideran como sus declaraciones tejidas. Las relaciones incluyen la generalización, la asociación y la dependencia. En el caso de que el tejedor de aspectos utilice el tipo de tejidos, es decir, introduzca un aviso tejido en AspectJ, éste se especifica como una restricción para la correspondiente declaración tejida. El aspecto se representa como un rectángulo de clase con el estereotipo <<aspecto>> (Figura 18) . En el apartado de la lista de operaciones se muestran las declaraciones tejidas. Cada tejido se muestra como una operación con el estereotipo <<tejido>>. Una signatura de la declaración tejida representa un designador cuyos elementos están afectados por el aspecto. Figura 18. Representación de un aspecto en UML extendido <<aspecto>> Nombre_Aspecto Atributos_aspecto operaciones_aspecto Clasificador Clase Nodo Interfaz Componente Aspecto TipoDato Visión General de la Programación Orientada a Aspectos 35 Relación aspecto-clase. En el metamodelo de UML se definen tres relaciones básicas, que se derivan del elemento Relación del metamodelo: Asociación, Generalización y Dependencia.[20] . La relación entre un aspecto y su clase es un tipo de relación de dependencia. La relación de dependencia modela aquellos casos en que la implementación o el funcionamiento de un elemento necesita la presencia de otro u otros elementos. En el metamodelo UML del elemento Dependencia se derivan otros cuatro que son: Abstracción, Ligadura, Permiso y Uso (Figura 19). La relación aspecto-clase se clasifica como una relación de dependencia del tipo abstracción. Figura 19. Tipos de relaciones del metamodelo de UML Una relación de dependencia de abstracción relaciona dos elementos que se refieren al mismo concepto pero desde diferentes puntos de vista, o aplicando diferentes niveles de abstracción. El metamodelo UML define también cuatro estereotipos [20] para la dependencia de abstracción: derivación, realización, refinamiento y traza. Con el estereotipo <<realiza>> se especifica una relación de realización entre un elemento o elementos del modelo de especificación y un elemento o elementos del modelo que lo implementa. El elemento del modelo de implementación ha de soportar la declaración del elemento del modelo de especificación. Relación Generalización Dependencia Asociación Ligadura Abstracción Uso Permiso Visión General de la Programación Orientada a Aspectos 36 La relación aspecto-clase se recoge en la dependencia de abstracción con el estereotipo realización (<<realiza>>). Esta relación se representa en la Figura 20. Figura 20. Notación de la relación aspecto-clase Clase tejida. Al utilizar un tejedor de aspectos, el código de las clases y los aspectos se mezclan y se genera como resultado una clase tejida. La estructura de la clase tejida depende del tejedor de aspectos y del lenguaje de programación que se haya utilizado. Por ejemplo, como se ha visto en el apartado Tejiendo clases y aspectos, AspectJ reemplaza la clase original por la clase tejida, mientras que AOP/ST genera una clase tejida que se deriva de la clase original. La solución que proponen Suzuki y Yamamoto para modelar esto es introducir el estereotipo <<clase tejida>> en el elemento Clase para representar una clase tejida. Se recomienda que en la clase tejida se especifique con una etiqueta la clase y el aspecto que se utilizaron para generarla. En la Figura 21 se representa la estructura de las clases tejidas, utilizando tanto el tejedor AspectJ como el tejedor AOP/ST. <<aspecto>> Nombre_Aspecto Atributos_aspecto operaciones_aspecto Nombre_Clase Atributos_clase operaciones_clase <<realiza>> Visión General de la Programación Orientada a Aspectos 37 Figura 21. Estructura de las clases tejidas. <<aspecto>> Aspecto-1 AppClase <<realiza>> Estructura base <<clase tejida>> AppClaseTejida Estructura te j ida As p ectJ <<aspecto>> Aspecto-1 AppClase Estructura te j ida AOP/ST Visión General de la Programación Orientada a Aspectos 38 9. Disciplinas relacionadas con POA. Por último, hay muchas disciplinas y campos de investigación relacionados de alguna manera con la orientación a aspectos, algunos persiguen los mismos objetivos y algunos otros. En este apartado se hará mención a algunos de ellos, de manera que se tenga una visión más amplia de por dónde se mueve la investigación en esta disciplina. Reflexión y protocolos de metaobjetos Un sistema reflectivo proporciona un lenguaje base y uno o más metalenguajes que proporcionan control sobre la semántica del lenguaje base y la implementación. Los metalenguajes proporcionan vistas de la computación que ningún componente del lenguaje base puede percibir. Los metalenguajes son de más bajo nivel que los lenguajes de aspectos en los que los puntos de enlace son equivalentes a los “ganchos” de la programación reflectiva. Estos sistemas se pueden utilizar como herramienta para la programación orientada a aspectos. Transformación de programas El objetivo que persigue la transformación de programas es similar al de la orientación a aspectos. Busca el poder escribir programas correctos en un lenguaje de más alto nivel, y luego, transformarlos de forma mecánica en otros que tengan un comportamiento idéntico, pero cuya eficiencia sea mucho mejor. Con este estilo de programación algunas propiedades de las que el programador quiere implementar se escriben en un programa inicial. Otras se añaden gracias a que el programa inicial pasa a través de varios programas de transformación. Esta separación es similar a la que se produce entre los componentes y los aspectos. Programación subjetiva La programación subjetiva y la orientada a aspectos son muy parecidas, pero no son exactamente lo mismo. La programación subjetiva soporta la combinación automática de métodos para un mensaje dado a partir de diferentes sujetos. Estos métodos serían equivalentes a los componentes tal y como se perciben desde la POA. Hay una diferencia clave entre ambas disciplinas, y es que mientras que los aspectos de la POA tienden a ser propiedades que afectan al rendimiento o la semántica de los componentes, los sujetos de la programación subjetiva son características adicionales que se añaden a otros sujetos. Visión General de la Programación Orientada a Aspectos 39 10. Conclusiones La separación de los aspectos a todos los niveles (diseño, codificación y ejecutable) es un paso importante que se ha comenzado a dar, pero que aún hay que refinar, sobre todo en cuestiones de eficiencia. Los lenguajes de aspectos de dominio específico juegan un papel crucial en la programación orientada a aspectos y que son preferibles a los lenguajes de aspectos de propósito general, porque soportan mejor la separación de funcionalidades. Pero aún no tenemos experiencia suficiente con los lenguajes de aspectos. Los entornos de programación orientada a aspectos que han habido hasta ahora no soportan una separación de funcionalidades suficientemente amplia, o bien el lenguaje de aspecto soporta el dominio de aspecto parcialmente, o bien no se sostiene bien la restricción del lenguaje base. El campo de la orientación a aspectos es un campo de investigación muy joven, en el que se abre un horizonte bastante amplio. Desde la definición de los distintos aspectos mediante lenguajes de propósito específico, hasta middlewares orientados a aspectos, pasando por las etapas de análisis y diseño orientadas a aspectos, aún quedan muchos campos que abonar, por lo que se cree interesante el abrir nuevas líneas de investigación en esta parcela. Visión General de la Programación Orientada a Aspectos 40 11. Listado de Figuras Figura 1 Esquema de la evolución de la ingeniería del software .................................................5 Figura 2. Descomposición en aspectos ¿Una quinta generación? ..............................................7 Figura 3. Estructura de un programa orientado a aspectos.......................................................11 Figura 4 Comparativa de la forma de un programa tradicional con uno orientado a aspectos 11 Figura 5. Estructura de una implementación en los lenguajes tradicionales. ..............................14 Figura 6. Estructura de una implementación en los lenguajes de aspectos................................14 Figura 7. Estructura de clases resultado de tejer dos aspectos y una clase con el Tejedor AOP/ST................................................................................................................................16 Figura 8 Protocolo entre un objeto y su coordinador en COOL. ...............................................19 Figura 9. Aviso definido mediante un corte con nombre. ..........................................................22 Figura 10. Aviso definido mediante un corte anónimo. ..............................................................22 Figura 11 Código de la clase cola circular sin restricciones de sincronización........................25 Figura 12. Objeto cola circular con restricciones de sincronización.........................................26 Figura 13. Clase ColaCircular definida en el lenguaje base de COOL. ....................................27 Figura 14. Implementación de la cola circular utilizando un coordinador COOL. ...................28 Figura 15 Aspecto para definir estrategia de sincronización mediante AspectJ.......................29 Figura 16. Clase ColaCircular para un aspecto en AspectJ.......................................................30 Figura 17. El Aspecto como un elemento del metamodelo UML derivado de Clasificador ......34 Figura 18. Representación de un aspecto en UML extendido....................................................34 Figura 19. Tipos de relaciones del metamodelo de UML ...........................................................35 Figura 20. Notación de la relación aspecto-clase.......................................................................36 Figura 21. Estructura de las clases tejidas. ................................................................................37 Visión General de la Programación Orientada a Aspectos 41 12. Referencias [1] G. Kiczales, J. Lamping, A. Mendhekar, C. Maeda, C. Lopes, J. Loingtier and J. Irwin, Aspect-Oriented Programming, Xerox Palo Alto Research Center, 1997 [2] C. I. V. Lopes, D: A Language Framework for Distributed Programming, Ph.D. thesis, Northeastern University, Nov. 1997 [3] F. Matthijs, W. Joosen, B. Vanhaute, B. Robben, P. Verbaeten. “Aspects should not die.” In European Conference on Object-Oriented Programming, Workshop on AspectOriented Programming. 1997. [4] K. Mens, C. Lopes, B. Tekinerdogan, and G. Kiczales. Aspect-oriented programming workshop report, 1997. [5] K. Mehner, A. Wagner, An Assesment of Aspect Language Design,K University of Paderborn. First International Symposium on Generative and Component-Based Software Engineering (GSCE’ 99) Young Researchers Workshop Abstracts [6] J. Lamping. The Interaction of Components and Aspects. Position paper at the ECOOP’97 workshop on Aspect Oriented Programming, 1997. [7] http://www.ccs.neu.edu/research/demeter/biblio/dem-book.html [8] Lea D. Concurrent Programming in JavaTM: design principles and patterns. Addison-Wesley, 1996. [9] J. Jaworski. JavaTM. Guía de Desarrollo. Prentice Hall, 1996. [10] J. Lamping. The role of the base in the aspect oriented programming. Position paper in the European Conference on Object-Oriented Programming Workshop, 1999. [11] K. Boellert. On Weaving Aspect. Position papers in the European Conference on Object-Oriented Programming Workshop, 1999. [12] AspectJ Home Page: http://www.parc.xerox.com/aop/aspectj/ [13] G. Kiczales, C. Lopes. Aspect-Oriented Programming with AspectJTM –Tutorial-. Xerox Parc. http://www.aspectj.org. [14] AspectJTM : User’s Guide. Xerox Parc Corporation. http://www.aspectj.org. [15] AspectJTM . Código de ejemplo. Xerox Parc Corporation. http://www.aspectj.org. [16] AOP/ST Home Page: http://www.germany.net/teilnehmer/101,199268/ [17] Kai Böllert. Aspect-Oriented Programming. Case Study: System Management Application. Graduation thesis, Fachhochschule Flensburg, 1998. [18] C. Videira Lopez, G. Kiczales. Recent Developments in AspectJTM. Position paper at the ECOOP’98 workshop on Aspect Oriented Programming, 1998.