Sunteți pe pagina 1din 32

Universidad Tecnológica de Panamá

Facultad de Ingeniería de Sistemas Computacionales

Ambientes de Programación

“Programación Orientada a Aspectos”

Integrantes:

Janice Martínez
Steven Martínez
Néstor Martínez
Abdiel Martínez

Profesora: Yadira Martínez

Martes 18 de Agosto de 2009.


Introducción.........................................................................................................................3
Separación de Asuntos (Separation of Concerns)................................................................4
Programación Orientada a Aspectos....................................................................................7
Lenguajes Programación Orientado a Aspectos................................................................11
AspectJ...............................................................................................................................11
AspectJ Puntos de Unión...............................................................................................13
Aplicación de AspectJ........................................................................................................14
AspectJ y las Fases de Desarrollo......................................................................................15
Diseño........................................................................................................................15
Implementación..........................................................................................................16
Fase de Pruebas..........................................................................................................18
Ejemplo de AspectJ............................................................................................................18
Requerimientos..............................................................................................................18
Implementación Java.................................................................................................19
Implementación en C#...............................................................................................21
Implementacion C# con registro de sucesos en consola............................................23
Implementación usando AspectJ................................................................................25
Implementación de Aspecto en .Net C#............................................................................27
Ejemplo de Rastreo (Tracing) de Alto Nivel por Aspecto.........................................27
Implementación de XTraceMethodBoundaryAttribute.............................................28
Inicialización en Tiempo de Compilación.................................................................28
Métodos en Tiempo de Ejecución..............................................................................30
Referencias.........................................................................................................................32

2
Introducción
La industria de desarrollo de software ha evolucionado grandemente en los últimos
años. Algunas veces como desarrollador es difícil mantenerse al día con las nuevas
tecnologías o procesos que cada día se incorporan. Sin embargo a pesar de todos los
avances que se están dando existen problemas para los cuales todavía no se tiene una
solución ideal. Uno de estos problemas es la correcta modularización, en especial,
cuando se lidia con un sistema complejo y grande. La adopción de OOP (siglas en
ingles) ha ayudado grandemente a que se avance para subsanar este dilema; sin
embargo, no es suficiente. La programación orientada a aspectos va un paso más allá y
aunque lejos de ser una solución ideal, subsana algunos deficiencias del modelado que
se logra por medio de OOP .

3
Separación de Asuntos (Separation of Concerns)
El principio de Separación de Asuntos fue propuesto en la década de 1970 por Edsger
W. Dijkstra en su ensayo: “Rol del pensamiento Científico”. En este ensayo Esdger
establecía que un problema dado debe ser separado en varios aspectos o asuntos.
Quince años después es evidente que el concepto de separación de asuntos es
grandemente adoptado por los profesionales de la computación. Como prueba de esto
podemos citar el libro publicado por Chris Reade “Elementos de Programación
Funcional”. En el cuales se describe la separación de asuntos de manera mas
detallada. Reade declara que el programador debe ser capaz de hacer varias funciones
a la vez:

- Describir lo que se va a computar

- Organizar la secuencia de computación en pequeños pasos

- Organizar la administración de la memoria durante la computación.

Como ya lo estableció Esdger en su ensayo, el proceso de separar un problema en la


suma de sus partes no es algo nuevo. El mismo se aplica en áreas diferentes a la
programación o en otras ingenierías. Como la construcción de objetos más tangibles,
por ejemplo podemos citar la construcción de un helicóptero

4
Podemos definir asuntos como los diferentes temas o asuntos de los que es necesario
ocuparse para resolver el problema. Una de ellos es la función específica que debe
realizar una aplicación, pero también surgen otros como por ejemplo distribución,
persistencia, replicación, sincronización, etc. Usualmente asuntos son equivalentes con
características o comportamientos del programa.

La Separación de Asuntos es la base que promovió el desarrollo de los lenguajes


orientados a objetos, en donde dado un buen diseño es posible alcanzar mayores
niveles de reutilización, independencia. Con miras a conseguir una mayor productividad,
también se han creado patrones de desarrollo que ayudan a alcanzar una mejor
separación de asuntos. Podemos mencionar Modelo – Vista- Controlador(Model View
Controller).

Las técnicas de modelado utilizadas actualmente en la etapa de diseño de un sistema


se basan en partirlo en varios subsistemas que resuelvan parte del problema o
correspondan a una parte del dominio sobre el que trata. Estas técnicas sufren en su
mayoría la llamada "tiranía de la descomposición dominante" que consiste en
guiarse al modelar, implícita o explícitamente, por una visión jerárquica determinada de
la organización del sistema.

El problema de estas técnicas es que no contemplan el hecho que hay puntos de


funcionalidades (usualmente la funcionalidades no principales del sistema) que se van a
repetir dentro de la jerarquía en secciones que no tienen relación entre sí. De tal
manera que estos puntos no se adaptan al principio básico de la separación de asuntos.
Actualmente la solución es repetir el código, lo que introduce una serie de problemas

5
como incremento en el costo de mantener el software, decremento en la reusabilidad,
entre otros.

Estas responsabilidades, que aparecen diseminadas en el código atravesando partes


del sistema no relacionadas en el modelo, se denominan asuntos transversales. Las
técnicas de modelado convencional no separan bien los asuntos transversales (cross-
cutting concern). Incluso los lenguajes orientados a objetos, a pesar de las muchas
ventajas que nos presentan sufren de esta desventaja en particular.

Los asuntos transversales dan lugar a código disperso y enredado (scattering and
tangling). En el caso de scattering en lugar que un componente se enfoque en
desarrollar un solo asunto, el componente desarrolla varios asuntos. Cómo
desarrollador debemos conocer sobre todos los asuntos que se están desarrollando en
el componente. Incrementando la curva de aprendizaje del código de la aplicación.

Los asuntos transversales se pueden clasificar en: Compañeros (Peers). Estos son
asuntos que se distinguen claramente uno de otros. Los mismos tienen el mismo nivel
de importancia. En el ejemplo familiar del ATM, retiro de efectivo, depósito,
transferencia de fondos todos son asuntos transversales compañeros (Peers).1 Estos
asuntos se pueden desarrollar como sistemas independientes, sin embargo al
desarrollarse dentro del mismo sistema nos encontramos con el hecho de que existe
sobreposición en la codificación de los mismos.

La otra categoría de asuntos transversales se denomina extensiones. Las extensiones


son componentes que se construyen sobre una base. Usualmente representan servicios
o características adicionales. Mantener las extensiones separadas nos permite que un
problema complejo sea entendible. El problema con las extensiones radica en que se
necesita incluir un llamado de las mismas en el componente base. Al hacer esto se ha
hecho un cambio intrusivo en el código base. Es decir el componente base estaba
completo antes de que decidiera añadir una nueva característica por medio de las
extensiones. El código con el cual se une un componente base a una extensión se

1
(Jacobson, et al., 2004)

6
denomina código de unión (glue code). No importa que tan bueno sea el diseño, con las
técnicas de modelado convencionales, el código de unión es necesario.

Un ejemplo clásico de asuntos transversales es el “logging” o registro de sucesos en


una aplicación. El registro de sucesos no es parte de la funcionalidad principal de la
aplicación y el mismo está diseminado prácticamente en todos los módulos de una
aplicación. En la Figura 2 es una representación gráfica de los distintos componentes
de una aplicación. Las secciones en rojo representan los llamados que se hacen al
código que graba los sucesos; los rectángulos representan los diferentes módulos de la
aplicación. Como se puede apreciar en la figura el código de registro de sucesos está
diseminado por diferentes componentes.

Programación Orientada a Aspectos


Las técnicas convencionales de modelado son un gran herramienta para estructurar un
sistema complejo de manera jerárquica; sin embargo, como ya hemos establecidos
presentan limitaciones.

Para superar estas limitaciones es necesaria una nueva metodología modular que nos
permita: alcanzar verdadera separación de los asuntos, incluyendo los asuntos
transversales, en todas las fases de desarrollo de software (requerimientos, análisis,

7
diseño, codificación y pruebas). Está nueva metodología también debe permitir la
composición de asuntos de manera coherente e integral.

Programación orientada a aspectos es una de las respuestas al problema de los


asuntos transversales. La programación orientada a aspectos son una serie de
tecnologías que tratan de proveer una mejor solución a la separación de asuntos
transversales. La solución que la AOP (Aspect Oriented Programming) presenta es la
modularización de los asuntos transversales. La modularización de los asuntos
transversales se denomina aspectos.

Debido a la escasa literatura en español sobre el tema, se presenta la terminología


original en inglés.

• Aspect (Aspecto) es una funcionalidad transversal (cross-cutting) que se va a


implementar de forma modular y separada del resto del sistema. El ejemplo más
común y simple de un aspecto es el logging (registro de sucesos) dentro del
sistema, ya que necesariamente afecta a todas las partes del sistema que
generan un suceso.

• Join point (Punto de Cruce o de Unión) es un punto de ejecución dentro del


sistema donde un aspecto puede ser conectado, como una llamada a un
método, el lanzamiento de una excepción o la modificación de un campo. El
código del aspecto será insertado en el flujo de ejecución de la aplicación para
añadir su funcionalidad.

• Advice (Consejo) es la implementación del aspecto, es decir, contiene el código


que implementa la nueva funcionalidad. Se insertan en la aplicación en los
Puntos de Cruce.

8
• Pointcut (Puntos de Corte) define los Consejos que se aplicarán a cada Punto
de Cruce. Se especifica mediante Expresiones Regulares o mediante patrones
de nombres (de clases, métodos o campos), e incluso dinámicamente en tiempo
de ejecución según el valor de ciertos parámetros. Un ejemplo sencillo se puede
aplicar a clases que representen figuras geométricas. En

• Introduction (Introducción) permite añadir métodos o atributos a clases ya


existentes. Un ejemplo en el que resultaría útil es la creación de un Consejo de
Auditoría que mantenga la fecha de la última modificación de un objeto,
mediante una variable y un método setUltimaModificacion(fecha), que podrían
ser introducidos en todas las clases (o sólo en algunas) para proporcionarlas
esta nueva funcionalidad.

• Target (Destinatario) es la clase aconsejada, la clase que es objeto de un


consejo. Sin AOP, esta clase debería contener su lógica, además de la lógica
del aspecto.

• Proxy (Resultante) es el objeto creado después de aplicar el Consejo al Objeto


Destinatario. El resto de la aplicación únicamente tendrá que soportar al Objeto
Destinatario (pre-AOP) y no al Objeto Resultante (post-AOP).

• Weaving (Tejido) es el proceso de aplicar Aspectos a los Objetos Destinatarios


para crear los nuevos Objetos Resultantes en los especificados Puntos de
Cruce. Este proceso puede ocurrir a lo largo del ciclo de vida del Objeto
Destinatario:

o Aspectos en Tiempo de Compilación, que necesita un compilador


especial.

o Aspectos en Tiempo de Carga, los Aspectos se implementan cuando el


Objeto Destinatario es cargado. Requiere un ClassLoader especial.

9
o Aspectos en Tiempo de Ejecución.2

2
(Wikimedia Foundation, 2009)

10
Lenguajes Programación Orientado a Aspectos
Como toda investigación lleva a una aplicación práctica la programación orientada a
aspectos no es la excepción. Existen varios lenguajes de programación que han
implementado, algunos más exitosos que otros, técnicas para poder aplicar los
aspectos. Algunos de estos programas son específicos a un dominio y no se pueden
usar como lenguajes multi-propósitos. Otros han sido iniciativas de universidades y al
carecer del respaldo económico no han tenido la adopción esperada, quedando en las
fases experimentales. Cómo un ejemplo de este último caso se puede mencionar a
Aspect.Net.

Aspect.Net es una implementación de programación orientada a aspectos para el


Framework .Net promovido por Vladimir O. Safonov de la Universidad de San
Petesburgo. Aspect.Net está conformado por dos herramientas:

Editor de Aspectos: permite añadir nuevos aspectos, explorar y seleccionar o no los


posibles puntos de unión.

Tejedor (Weaver): estadísticamente aplica el consejo (advice) a los puntos de unión que
se establece basado en las reglas descritas en el aspecto.

La implementación de Aspect.Net no utiliza reflexión (conjunto de clases que puede


inferir información sobre las mismas clases, métodos, etc.; incluyendo llamados
dinámicos), ya que los autores consideraron que el uso de reflexión no era suficiente
para la integración de los aspectos. Hay que resaltar que la iniciativa de Aspect.Net se
inicio en el 2005 y el Framework de .Net ha avanzado desde esa fecha. Sin embargo el
proyecto actual de Aspect.Net sólo es compatible con el Visual Estudio 2005 y el mismo
no es fácil de conseguir, a pesar de ser gratis. El autor todavía considera que está en
una fase experimental.

AspectJ
Es una extensión multi-propósito de Java que implementa la programación orientada a
aspectos. Está formada por una serie de herramientas que permiten a los
desarrolladores de Java administrar mejor los problemas inherentes de los sistemas

11
grandes y en general aplicar mejor la modularidad dentro de los mismos, en especial
aplicar correctamente la separación de los asuntos transversales. Originalmente fue
desarrollado por profesionales del Centro de Investigación de Palo Alto de Xerox, ahora
es parte del proyecto Eclipse.

Es un lenguaje práctico que provee, en un paquete compatible con Java, un set sólido
de características de Programación Orientada a Aspectos. No está diseñado para ser
una encarnación en blanco de las ideas de Programación Orientada a Aspectos, no es
una implementación formal ni un estudio agresivo del dominio y las oportunidades de la
POA.

AspectJ es una extensión compatible de Java que permite el manejo de asuntos


transversales (crosscutting concerns). Se entiende por compatibilidad los siguientes 4
puntos.

- Compatibilidad ascendente: todos los programas de Java legales deben ser


programas legales de AspectJ.

- Compatibilidad de plataforma: todos los programas legales de AspectJ deben


correr en máquinas virtuales estándares de Java.

- Compatibilidad de herramienta: debe ser posible extender herramientas actuales


para que soporten AspectJ; esto incluye IDEs, herramientas de documentación,
herramientas de diseño, etc.

- Compatibilidad de Programación: programar en AspectJ debe de ser como una


extensión natural de programar con Java.

AspectJ soporta dos implementaciones para los asuntos transversales. La primera se


denomina asuntos transversales dinámicos (dynamic crosscutting); por medio de este
mecanismo es posible definir implementaciones adiciones que sean ejecutadas en
puntos bien definidos de una aplicación. La otra implementación se llama asuntos

12
transversales estáticos (static crosscutting). En ella se definen nuevas operaciones en
tipos existentes.

AspectJ Puntos de Unión


Los puntos de unión (Joint points) son un elemento crítico en el diseño de cualquier
mecanismo de un lenguaje orientado a aspectos. AspectJ no es la excepción. Este
modelo provee un marco de referencia que hace posible la ejecución coordinada de
código de aspectos junto con código que no hace referencia a los aspectos. La
implementación de los asuntos transversales dinámicos se logra mediante los puntos de
unión.

Los diferentes tipos de puntos de unión que AspecJ tiene se presentan en la siguiente
tabla:

Tabla 2.1 Dummy

Tipo de Punto de Unión Momento de ejecución en el Programa en


el cual…

- Llamada de un método El método (o el constructor de la clase) es


llamado. Los puntos de unión son el objeto

- Llamade de un constructor que hace el llamado. Si el método es


estático entonces es en ningún objeto.

- Recepción de la llamada de un Un objeto recibe el llamado de un método


método o un constructor. Puntos de unión de
recepción se dan antes que el método o

- Recepción del llamado de un constructor se haya despachado. Ejemplo;


constructor ellos ocurren dentro del objeto llamado, en
un punto del flujo de control después que
el control ha sido transferido al objeto que

13
hace el llamado, pero antes que cualquier
método/constructor haya sido llamado.

- Ejecución de un método Un método individual o constructor es


invocado.

- Ejecución de un constructor

- Lectura de una propiedad Una propiedad de un objeto, clase o


interfase es leída.

- Escritura de una propiedad Una propiedad de un objeto, clase o


interfase es escrita.

- Ejecución de un manejo de El manejo de excepción es invocado.


excepción

- Inicialización de una clase Inicializadores estáticos de una clase, si


existes, son ejecutados.

- Inicialización de un objeto Cuando los inicializadotes dinámicos de


una clase, si están presentes, son
ejecutados durante la creación de un
objeto

Aplicación de AspectJ
Estudiar la Solución Convencional primero —En este paso, se diagrama,
diseña, e incluso se crea un prototipo de la solución convencional. La solución
convencional resalta las ventajas de la solución que provee AspectJ, el propósito
principal es ayudar a entender el diseño necesario para una mejor solución con

14
AspectJ. La idea es identificar el código disperso y enredado (scattering and tangling) y
luego modularizarlo. Una vez se logra suficiente experiencia en este paso, se puede
reducir la importancia del mismo o incluso eliminarlo..

2 Limitar la implementación—Al limitar la solución a solo módulos que


actualmente necesitan la funcionalidad, se elimina el impacto, tanto positivo como
negativo, en otros módulos. El objetivo es dejar la mayor cantidad del sistema sin
afectar como sea posible y reducir el esfuerzo necesario en las pruebas. Para hacer
esto, se pueden usar puntos de corte como within() para especificar sólo puntos de
unión en los módulos donde se quiera hacer aplicar los aspectos. Como alternativa se
puede configurar el sistema para que sólo utilice eso módulos.

3 Generalizar la Solución—Una vez se haya probado ampliamente la solución y


estudiado su impacto, se debe modificar los puntos de corte o cambiar la configuración
que ha estado limitando el área de aplicación de la solución. Por ejemplo, en lugar de
restringir el registro de sucesos a ciertos módulos, se remueven las restricciones para
que la solución se aplique en todo el sistema. De esta manera nuevos módulos se
beneficiarán de esta solución de manera inmediata apenas ingresen al sistema, sin
necesidad de esfuerzo adicional. La única manera en que el paso tres se puede concluir
exitosamente es mediante la aplicación asertiva de los pasos 1 y 2. Tanto el diseño
como una prueba contenida de la solución son las bases para la implementación exitosa
de aspectos a una aplicación existente. 3

AspectJ y las Fases de Desarrollo


Cada fase de desarrollo (diseño, implementación, pruebas, manutención), registra una
serie de actividades de tal manera que la aplicación práctica en el uso de AspectJ varía
en cada una.

Diseño
Al igual que cualquier proceso de desarrollo de software un buen diseño es la piedra
angular para que el proyecto se desarrolle de manera exitosa; diseñar software usando
AspectJ no es la excepción a la regla. Al considerar el uso de AspectJ desde las etapas
3
(Laddad, 2003)

15
iníciales del diseño se obtiene los mayores beneficios de la utilización del mismo, ya
que se garantiza que los asuntos transversales son manejados correctamente e
integrados efectivamente por medio de los aspectos.

La manera típica de utilizar AspectJ en la fase de diseño incluye:

- Reconocer los asuntos transversales. Este paso es parte del mapeo necesario
de requerimientos a módulos. Una manera sencilla de identificar estos asuntos
es la utilización de frases como: “cada vez”, “en todo lugar”. Si nos encontramos
utilizando estas frases para describir parte de las funcionalidades en distintos
módulos, tenemos ante nosotros un asunto transversal. Reconocer los asuntos
transversales a tiempo, permite que estos sean delegados a aspectos que se
integran fácilmente a los módulos existentes.

- Diseñar los asuntos principales primero. Se debe aplicar técnicas estándares


para diseñar los asuntos principales. Entre mejor se realice esta labor, será más
fácil aplicar los asuntos transversales. Se recomienda la utilización de interfaces
con roles claros.

- Diseñar los asuntos transversales. Implementar los asuntos transversales


primordiales. Es una buena idea también hacer un bosquejo de los asuntos
transversales que se hayan reconocido, pero no se necesiten inmediatamente.
De tal forma que no se trabaje de manera repetida en el diseño al considerarse
las posibles mejoras al mismo.

Como se puede observar diseñar para Programación orientada a aspectos no es muy


diferente que el diseño convencional. La utilización de AspectJ nos fuerza a entender
claramente el diseño de la aplicación y a acogernos a los estándares definidos para
lograr un diseño de calidad. Una vez se logre esto, la aplicación de aspectos que se
encarguen de integrar los asuntos transversales se hará de manera efectiva y sin costos
adicionales.

Implementación
En general el proceso de implementar un sistema utilizando AspectJ no es muy
diferente que cuando se utiliza un lenguaje de orientación a objetos. Hay algunas
consideraciones básicas en las cuales se debe hacer más énfasis como:

16
-Separar en módulos correctamente el código: idealmente cada operación debe
corresponder con una funcionalidad. La unidad granular para definir los puntos de unión
son los métodos. Es importante que cada método tenga una función clara y concisa. De
esta manera el aplicar un aspecto será pan comido.

-Uniformidad en la nomenclatura: Esta es una regla que se debe aplicar


independientemente si se está trabajando con AspectJ o no. Toma incluso más
relevancia cuando se utiliza AspectJ. Una de las ventajas de AspectJ es que se pueden
definir puntos de unión mediante el uso de comodines. Si somos consistentes con la
nomenclatura podremos aplicar eficientemente los puntos de unión.

Algunas otras consideraciones incluyen:

- Separar los asuntos principales de los asuntos transversales (Igual que en la


fase de diseño)

- Buscar activamente secciones de código que se presenten repetidas (scattering)


o enredadas (tangling).

- Identificar los puntos de unión. Una vez se ha determinado la existencia de un


asunto transversal, se tiene que identificar ya de una manera tangible los puntos
en código que requieren la aplicación del asunto transversal. De igual forma se
debe determinar cuál es la mejor manera de expresar el punto de corte que
contenga todos los puntos de unión identificados previamente.

- Elegir la tecnología correcta para implementar el asunto transversal. (API de


reglas, o el motor de reglas, etc). Esto dependerá de la arquitectura general del
sistema.

- Diseñar los aspectos. Se puede considerar utilizar patrones como plantillas para
diseñar los aspectos. También es necesario aplicar una nomenclatura
consistente a los aspectos en sí. Esto facilitará la aplicación e puntos de unión
dentro de los aspectos si así se requiera. Es válido también determinar si los
mismos aspectos se pueden reutilizar en otros sistemas que presenten los
asuntos transversales que se quieren resolver.

En general al igual que en diseño la implementación de los aspectos no es


estructuralmente diferente pues se basa en conceptos existentes aplicables a la

17
orientación a objetos. El aspecto adicional sería la creación del aspecto en sí. Se hace
mayor énfasis en mantener una nomenclatura uniforme e identificar todos los puntos de
unión mediante la definición de los puntos de corte.

Fase de Pruebas
AspectJ provee una serie de escenarios que ayudan a automatizar el proceso de
pruebas de la aplicación. Debido a la naturaleza no intrusiva de los aspectos se puede:

Crear casos de usos: Por medio del uso de aspectos privilegiados se puede acceder a
los elementos privados de una clase. Esto permite que se creen programas de prueba
sin que se modifiquen la clase original que se quiere probar. Una vez se haya
garantizado que las pruebas dan los resultados esperados, los aspectos se pueden
sacar del sistema fácilmente.

Pruebas de desempeño: Para tal fin se pueden crear aspectos que ayuden a monitorear
el desempeño de la aplicación cuando este en la versión beta.

Reporte de errores: se pueden usar aspectos que brinden información adicional acerca
del contexto de los errores. El contexto no sólo incluye el stacktrace sino toda la
información que esté disponible en el punto de unión donde se dio el error. En este
sentido los reportes de errores son superiores a la información estándar que proveen
los compiladores.

Ejemplo de AspectJ
Haciendo alusión al asunto transversal sobre registros de sucesos. Presentamos el
siguiente ejemplo. Es una pequeña aplicación que tiene como objetivo simular algunos
flujos existentes en un salón de clases.

Requerimientos
Los requerimientos del micro sistema son:

- Permitir a los estudiantes registrar clases

- Permitir a los profesores asignar tareas

- Permitir a los profesores fracasar estudiantes

- Listar la lista de tareas asignadas a una clase

18
- Listar la lista de estudiantes registrados en una clase

- Permitir al estudiantes que se retire de la clase

- Asignar a un profesor con determinada clase

- Registrar los sucesos que ocurran en el sistema

Debido a que este es un ejemplo práctico enfocado en mostrar las diferencias básicas y
ventajas de utilizar AspectJ, la parte gráfica del micro sistema no será desarrollada. El
ejemplo se enfocará en crear las clases necesarias que satisfagan los requerimientos
antes señalados.

El siguiente diagrama de clases muestra el diseño inicial con el cual se pretende cumplir
con todos los requerimientos antes mencionados.

Como se puede observar en el diagrama la clase principal del sistema es la clase Class.
A continuación podemos ver la implementación del código en AspectJ y posteriormente
la implementación del código en C#.

Implementación Java
package com.object.model;

import java.util.ArrayList;
import java.util.*;

public class Class {

private ArrayList<Task> ActiveTasks;


private ArrayList<Student> RegisteredStudents;

19
private String _ClassName;

private Professor _Professor;

public Class()
{
ActiveTasks = new ArrayList<Task>();
RegisteredStudents = new ArrayList<Student>();
}

public void SetClassName(String name)


{
_ClassName = name;
}
public String GetClassName()
{
return _ClassName;
}

public void AddStudent(Student newStudent)


{
RegisteredStudents.add(newStudent);
}

public void RemoveStudent(Student newStudent)


{
RegisteredStudents.remove(newStudent);
}

public void AddTask(String name, Date dueDate)


{
Task newTask = new Task();
newTask.SetName(name);
newTask.SetDueDate(dueDate);
ActiveTasks.add(newTask);
}

public void RemoveTask(Task taskToRemove)


{
if(ActiveTasks.contains(taskToRemove))
{
ActiveTasks.remove(taskToRemove);
}
}

public void ListStudents()


{
for(int i = 0; i < RegisteredStudents.size(); i ++)
{
System.out.println("Student name: "
+ RegisteredStudents.get(i).GetName()
+ " " + RegisteredStudents.get(i).
GetLastName() );
}
}

20
public void ListTasks()
{
for(int i = 0; i < ActiveTasks.size(); i ++)
{
System.out.println("Task name: "
+ ActiveTasks.get(i).GetName()
+ " Due date: " + ActiveTasks.get(i).
GetDueDate().toString() );
}
}

public void SetProfessor(Professor newProfessor)


{
_Professor = newProfessor;
}

public Professor GetProfessor()


{
return _Professor;
}

Implementación en C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ObjectModel
{
public class Class
{
List<Student> RegisteredStudents;
List<Task> ActiveTasks;
Professor _Professor;

public string ClassName


{
get;
set;
}

public Class()
{
RegisteredStudents = new List<Student>();
ActiveTasks = new List<Task>();
}

public void AddStudent(Student newStudent)


{
RegisteredStudents.Add(newStudent);

21
}

public void RemoveStudent(Student newStudent)


{
RegisteredStudents.Remove(newStudent);
}

public void AddTask(string name, DateTime dueDate)


{
Task newTask = new Task();
newTask.Name = name;
newTask.DueDate = dueDate;
ActiveTasks.Add(newTask);
}

public void RemoveTask(Task taskToRemove)


{
if (ActiveTasks.Contains(taskToRemove))
{
ActiveTasks.Remove(taskToRemove);
}
}
public void ListTasks()
{
foreach (var tempTask in ActiveTasks)
{
Console.WriteLine("Taks name: " + tempTask.Name + " due
date " + tempTask.DueDate);
}
}
public void ListStudents()
{
foreach (var tempStudent in RegisteredStudents)
{
Console.WriteLine("Student name: " + tempStudent.Name +
" " + tempStudent.LastName);
}
}

}
}

Las dos implementaciones son muy parecidas y en apariencia cumplen con los
requerimientos señalados. Existen algunas diferencias inherentes al syntaxis propios de
los lenguajes, pero en teoría la implementación es la misma. No obstante, si
observamos cuidadosamente nos podemos percatar que ninguna de las dos
implementaciones cumple con el requerimiento sobre registrar todos los sucesos que se
esten realizando en el sistema. Necesitamos hacer unos ajustes en ambas
implementaciones para satisfacer todos los requerimientos.

22
Implementacion C# con registro de sucesos en consola
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ObjectModel
{
public class Class
{
List<Student> RegisteredStudents;
List<Task> ActiveTasks;
Professor _Professor;

public string ClassName


{
get;
set;
}

public Class()
{
Console.WriteLine("CTor init");
RegisteredStudents = new List<Student>();
ActiveTasks = new List<Task>();
Console.WriteLine("CTor end");
}

public void AddStudent(Student newStudent)


{
Console.WriteLine("AddStudent init");
RegisteredStudents.Add(newStudent);
Console.WriteLine("AddStudent init");
}

public void RemoveStudent(Student newStudent)


{
Console.WriteLine("RemoveStudent init");
RegisteredStudents.Remove(newStudent);
Console.WriteLine("RemoveStudent end");
}

public void AddTask(string name, DateTime dueDate)


{
Console.WriteLine("AddTask init");
Task newTask = new Task();
newTask.Name = name;
newTask.DueDate = dueDate;
ActiveTasks.Add(newTask);
Console.WriteLine("AddTask end");
}

23
public void RemoveTask(Task taskToRemove)
{
Console.WriteLine("RemoveTask init");
if (ActiveTasks.Contains(taskToRemove))
{
ActiveTasks.Remove(taskToRemove);
}
Console.WriteLine("RemoveTask end");
}
public void ListTasks()
{
Console.WriteLine("ListTasks init");
foreach (var tempTask in ActiveTasks)
{
Console.WriteLine("Taks name: " +
tempTask.Name + " due date "
+ tempTask.DueDate);
}
Console.WriteLine("ListTasks end");
}

public void ListStudents()


{
Console.WriteLine("ListStudents init");
foreach (var tempStudent in RegisteredStudents)
{
Console.WriteLine("Student name: "
+ tempStudent.Name + " " + tempStudent.LastName);
}
Console.WriteLine("ListStudents end");
}

}
}

Como se observa en la nueva implementación, se ha tenido que agregar varías líneas


de código para poder cumplir con el requerimiento sobre registro de sucesos. Han sido
necesario agregar más de diez líneas de código para poder implementarlo. Ahora bien
esto sólo ha sido en una clase. Es decir que todas las clases de la aplicación deberán
ser modificadas con llamados similares. Se puede discutir que es un proceso mecánico,
que solo es copiar y pegar lo que lo hace fácil de implementar. No obstante este no es
el único problema que esta implementación presenta. Supongamos que se decide
cambiar de estrategia y ya no se quiera desplegar los registros de sucesos en consola,
sino que se quieran grabar en un archivo de texto o en una base de datos. Habría que
cambiar todos los llamados en las clases. Este proceso es ineficiente y distrae al
programador de la implementación de las funcionalidades principales.

24
Implementación usando AspectJ
package com.object.model;
import org.aspectj.lang.*;
public aspect LoggingAspect {

pointcut traceMethods()
: execution(* *.*(..)) && !within(LoggingAspect);

before() : traceMethods() {
Signature sig = thisJoinPointStaticPart.getSignature();
String sigName = sig.getName();
String declaringName = sig.getDeclaringTypeName();
System.out.println(declaringName + " " + sigName + " init");

}
after() : traceMethods(){
Signature sig = thisJoinPointStaticPart.getSignature();
String sigName = sig.getName();
String declaringName = sig.getDeclaringTypeName();
System.out.println(declaringName + " " + sigName + " end");

}
}

Por medio del uso del aspecto LogginAspect podemos incluir registros de sucesos en
todas las clases. Hemos modularizado exitosamente el asunto transversal. Ya que hay
un sólo lugar donde se maneja el registros de suceso, sería muy sencillo cambiar el
método consola por log4j o la librería estándar de Java para registrar sucesos

AspectJ también nos presenta una herramienta muy útil con la que se pueden ver los
puntos de unión que cumplen la condición.

25
Por medio del uso de esta herramienta podemos tener una idea exacta de cuantos
puntos de unión se han identificado para cada uno de los puntos de corte.

26
Implementación de Aspecto en .Net C#
Para la implementación de la tecnología de programación orientada aspecto nosotros
encontramos una herramienta llamada PostSharp. Existen otras librerías que permiten
la implementación de esta tecnología en .Net. Esta herramienta nos permite reducir el
número de líneas de código en la mayoría (si no todos) de los lenguajes .Net
implementando aspecto.
PostSharp nos facilita la implementación en nuestro código encapsulando los aspectos
como atributos personalizados, haciendo nuestra vida más fácil y la implementación
más placentera.

Ejemplo de Rastreo (Tracing) de Alto Nivel por Aspecto


En este ejemplo se demuestra el rastreo utilizando de Postsharp.
En este ejemplo se podrá hacer rastreo de excepciones, acceso a campos e invocación
de métodos.
También hace rastreo de los valores de los parámetros.
En el ejemplo ilustra cómo utilizarlo en tiempo de inicio de compilación.
Objetivo
Desarrollar los atributos personalizados
XTraceExceptionAttribute: escribe un mensaje cuando el método a la cual se aplica
falla con una excepción.
XTraceFieldAccessAttribute: escribe un mensaje cada vez que un campo a la cual es
aplicada es accesado, una operación get o set.
XTraceMethodBoundaryAttribute: escribe un mensaje cuando el método a la cual es
aplicada comienza o termina, ya sea exitoso o no.
XTraceMethodInvocationAttribute: escribe un mensaje cuando el mensaje a la cual
es aplicada es invocada (este método puede estar definido en otro assembly).
Nosotros queremos hacer rastreo de la forma más detallada posible, por ejemplo,
nosotros queremos escribir los valores de parámetros, valores de variables y más.
También queremos tomar ventaja de la inicialización en tiempo de compilación para
evitar formateando el texto a la hora de ejecutar.
Como los cuatro aspectos son muy similares, sómo vamos a comentar el que se utiliza
de forma más común, XTraceMethodBoundaryAttribute.

27
Implementación de XTraceMethodBoundaryAttribute
Como queremos insertar código antes y después del cuerpo del método, vamos a
derivar nuestro atributo personalizado de OnMethodBoundaryAspect e implementamos
los métodos OnEntry, OnSuccess y OnException.
El esqueleto de nuestro atributo personalizado es:
[Serializable]
public sealed class XTraceMethodBoundaryAttribute :
OnMethodBoundaryAspect
{

public override void OnEntry(MethodExecutionEventArgs context)


{
}

public override void OnSuccess(MethodExecutionEventArgs eventArgs)


{
}

public override void OnException(MethodExecutionEventArgs


eventArgs)
{
}

Inicialización en Tiempo de Compilación


Escribiendo el nombre con todos sus parámetros y los parámetros de tipo genérico no
es trivial. Requiere una potencia de cálculo que no podemos rechazar si queremos que
nuestro aspecto de trace un rendimiento de ejecución aceptable.
Hay muchas que se conocen en tiempo de compilación: el nombre del tipo y el método,
el número de parámetros genéricos y el número y tipo de parámetros. Así que
escogimos que en tiempo de compilación, se va a crear una plantilla (formatear cadena)
y en tiempo de ejecución, para formatear estas plantillas utilizando parámetros
concretos y parámetros genéricos recibidos.
Por ejemplo, si nosotros tenemos un método:
MyType<T1>::MyMethod<T2>(T2 arg1, int arg2)

la plantilla puede ser:


MyType<{0}>::MyMethod<{1}>({2}, {3})

En tiempo de ejecución, nosotros proveeremos un arreglo con los parámetros.

28
La funcionalidad de preparar plantillas y la implementación esta encapsulado en
las clases Formatter y MethodFormatStrings. Su implementación no es de
interés en nuestra discusión, así que no lo vamos a describir en nuestro ejemplo.
¿Cómo podemos realizar esto con PostSharp?
Primero, definimos las instancias de los campos que contienen las cadenas de
formateo y otras cosas que no queremos computar o calcular en tiempo de
ejecución. Después, implementamos el método CompileTimeInitialize para
configurar estos campos. Durante la post-compilación, PostSharp Laos
serializará el objeto. Eso significa que los campos que son inicializados en
tiempo de compilación para ser utilizados en tiempo de ejecución deben ser
serializables. Como este es el comportamiento predeterminado, no tenemos
que ponerle mucha atención a eso.

Finalmente aquí está el código de inicialización en tiempo de compilación:


string prefix;
MethodFormatStrings formatStrings;
bool isVoid;

public string Prefix


{
get { return this.prefix; }
set { this.prefix = value; }
}

public override void CompileTimeInitialize(MethodBase method)


{
this.prefix = Formatter.NormalizePrefix(this.prefix);
this.formatStrings = Formatter.GetMethodFormatStrings(method);
MethodInfo methodInfo = method as MethodInfo;
if (methodInfo != null)
{
this.isVoid = methodInfo.ReturnType == typeof(void);
}
else
{
this.isVoid = true;
}
}

29
Métodos en Tiempo de Ejecución
Vamos de vuelta a nuestro esqueleto de nuestra clase
XTraceMethodBoundaryAttribute. Tenemos que implementar los métodos
OnEntry, OnSuccess and OnException. Todo lo que tenemos que hacer es
formatear las plantillas con los parámetros concretos y llamar el método
Trace.TraceInformation.
public override void OnEntry(MethodExecutionEventArgs context)
{
Trace.TraceInformation(
this.prefix + "Entering " +
this.formatStrings.Format(
context.Instance,
context.Method,
context.GetArguments()));
Trace.Indent();

public override void OnSuccess(MethodExecutionEventArgs eventArgs)


{
Trace.Unindent();
Trace.TraceInformation(
this.prefix + "Leaving " +
this.formatStrings.Format(
eventArgs.Instance,
eventArgs.Method,
eventArgs.GetArguments()) +
(this.isVoid ? "" : Formatter.FormatString(" : {{{0}}}.",
eventArgs.ReturnValue)));
}

public override void OnException(MethodExecutionEventArgs eventArgs)


{
Trace.Unindent();
Trace.TraceWarning(
this.prefix + "Leaving " +
this.formatStrings.Format(
eventArgs.Instance,
eventArgs.Method,
eventArgs.GetArguments()
) +
Formatter.FormatString(" with exception {0} : {{{1}}}.",
eventArgs.Exception.GetType().Name,
eventArgs.Exception.Message));

30
Conclusión
Por medio del desarrollo de esta monografía se ha podido apreciar las ventajas que
AspectJ y PostSharp brindan en el desarrollo de un sistema. Descartando el esfuerzo
necesario para aprender sobre la sintaxis requerida para especificar los puntos de
cortes de los diferentes aspectos que se han querido introducir al sistema, el uso
correcto de garantiza un menor tiempo escribiendo código. Si bien es cierto que esta
labor sería un proceso mecánico, por medio de estos marcos de trabajo se puede
eliminar el mismo.

En cuanto al esfuerzo y tiempo necesario para diseñar la aplicación, AspectJ y


PostSharp requieren un poco más de tiempo en elaborar un diseño efectivo. No sólo es
necesario diseñar los componentes o módulos de la manera convencional, sino que
también se deben identificar los asuntos transversales de manera clara y concisa. De
igual forma se debe identificar los puntos de corte y la sentencia necesaria para
aplicarlos correctamente. En este proceso de diseño también se debe hacer énfasis en
las convenciones para nombrar las variables, clases y métodos. Ya que la uniformidad
en la nomenclatura nos facilitará la aplicación de puntos de corte por medio del uso de
comodines. La uniformidad en la nomenclatura debe ser en cualquier caso parte del
diseño convencional.

Para llegar a comprender mejor el abanico de oportunidades que AspectJ y PostSharp


nos presentan a la hora de diseñar y desarrollar un sistema sería necesario adquirir
mayor experiencia práctica con el mismo. Quizás entre las desventajas de desarrollar
con estos marcos de trabajo es que nos fuerzan a pensar en un mejor diseño, conocer
las clases con las que uno trabaja en caso de no ser clases propias y las relaciones
entre ellas. Este proceso no es inmediato y requiere una curva de aprendizaje elevada
si se pretende aprovechar todo el potencial de AspectJ y PostSharp.

31
Referencias
Gael Fraiteur. Bringing AOP to .NET, PostSharp.
[En línea]
http://www.postsharp.org/

An Overview of AspectJ. Kiczales, Gregor, y otros. 2001. Berlin : Springer


Berlin / Heidelberg, 2001. págs. 327-354. 978-3-540-42206-8.

Eisenberg, Andrew. 20009. Aspects Everywhere: Language Extensibility


for JDT through Equinox Aspects. [En línea] 23 de marzo de 20009. [Citado el:
21 de junio de 2009.] http://www.eclipsecon.org/2009/sessions?id=648.

González, Abdiel E. Cáceres. 2004. Programación Orientada a Aspectos.


Mexico : Centro de Investigación y de Estudios Avanzados Mexico, 2004.

Jacobson, Ivar y Ng, Pan-Wei. 2004. Aspect-Oriented Software


Development with Use Cases. s.l. : Addison Wesley Professional, 2004. 0-321-
26888-1.

Laddad, Ramnivas. 2003. AspectJ in Action - Practical Aspect-Oriented


Programming. New York : Manning, 2003.

Safonov, Vladimir. 2005. Aspect.NET: Aspect-Oriented Programming for


Microsoft.NET in Practice. [En línea] 28 de julio de 2005. [Citado el: 17 de
junio de 2009.] http://dotnet.sys-con.com/node/113337?page=0,1.

Wikimedia Foundation. 2009. Aspect Oriented Programming, Wikipedia.


[En línea] 2 de junio de 2009. [Citado el: 4 de junio de 2009.]
http://en.wikipedia.org/wiki/Aspect_oriented_programming.

—. 2009. Separation of Concerns. [En línea] 2 de mayo de 2009. [Citado el: 1


de junio de 2009.] http://en.wikipedia.org/wiki/Separation_of_concerns.

32

S-ar putea să vă placă și