POA [parte I] – Introducción a la programacion orientada a aspectos
POA (Programación Orientada a Aspectos) es un paradigma de programación que intenta incrementar la modularidad del software mediante una mejor estrategia de separation of concerns multidimensional.
Actualmente, los paradigmas de programación nos proveen de ciertas abstracciones (como procedimientos, clases, métodos, etc.) que nos permiten separar las incumbencias/concerns de un sistema en partes lo más cohesivas y menos acopladas posibles, con el objetivo de incrementar factores determinantes en la calidad del software (como la extensibilidad, mantenibilidad, reusabilidad, etc.).
El paradigma orientado a objetos por ejemplo, es uno de los más populares y utilizados hoy en día, ya que mediante técnicas como el encapsulamiento, herencia y poliformismo se adapta a la mayoría de los problemas de la realidad.
Sin embargo, a medida que la complejidad de los programas fue creciendo, se observó que hay ciertos concerns/incumbencias que no quedan correctamente separados en el paradigma orientado a objetos, sino que se diseminan de manera transversal por todo el sistema. Es decir, no se pueden separar claramente mediante las abstracciones que nos proveen los paradigmas de programación actuales.
Ejemplos patentes de esto, son aspectos como la sincronización, distribución, manejo de transacciones, gestión de seguridad, manejo de logs, etc. que todo programa medianamente complejo tiene, y que, como pueden ver, todos los componentes del sistema usan, dado que pertenecen a la infraestructura del software y no a la funcionalidad del mismo.
A estos aspectos, se los llama crosscutting concerns, o simplemente, aspectos.
La acoplación de estos aspectos con todos los componentes del sistema, como es de imaginar, afecta la modularidad de nuestros programas. Haciéndolos más difíciles de mantener, extender, etc.
El paradigma orientado a aspectos, nos permite, básicamente, aislar los aspectos (o crosscutting concerns) de los demás componentes del sistema.
Esta separación, implementada correctamente, nos provee de una mayor modularidad, y nos brinda muchos beneficios. Imagínense, cada lógica de negocio, sólo contendría lógica de negocio, nada de código de sincronización, transacciones, generación de logs, etc. El desarrollador podría ocuparse sólo de la lógica de negocio de la aplicación (abstrayéndose de los demás aspectos) o bien al revés, podría ocuparse de los aspectos sin tener que tocar la lógica de negocio.
La programación orientada a aspectos es un tema que despierta gran interés hoy en día. En este artículo vamos a ver una vista panorámica del tema, bueno… bastante panorámica… digamos, desde un avión a 9000 KM de altura… La idea es que se entienda el concepto de AOP, así que en este primer artículo vamos a ver que es AOP, como funciona en teoría, y un ejemplo muy sencillo con AspectJ.
Que es POA
Algo de historia
Antiguamente, antes de la programación procedural, los programas eran monolíticos, mezclaban funcionalidad y datos sin separación alguna (el conocido código “spaghetti”). A medida que el programa crecía en complejidad, era cada vez más difícil de entender y modificar.
Como consecuencia de esto, se empezó a aplicar una descomposición funcional, que separaba las diferentes funcionalidades del sistema en procedimientos y funciones.
Si bien este tipo de separación mejoraba la reusabilidad y claridad del código, seguía teniendo muchos problemas. Uno de los principales inconvenientes de este tipo de programación, era que, la mayoría de las veces, la modificación o adición de un dato implicaba la modificación de muchas funciones. Por lo que los programas a medida que crecían se hacían cada vez más difíciles de mantener y extender.
Para solucionar esto, apareció la programación orientada a objetos, una programación más evolucionada, que agrupa conjuntos de atributos y comportamientos inherentes del sistema en objetos, y utiliza técnicas como el encapsulamiento, herencia y poliformismo, adaptándose mucho mejor a los problemas de la realidad.
Pero el software siguió creciendo, y se observó que la POO no es suficiente. Se necesita un nuevo nivel de abstracción.
En el paradigma orientado a objetos, se pueden aislar claramente los componentes de lógica de negocio del sistema, sin embargo, los aspectos de infraestructura (como la sincronización, distribución, manejo de transacciones, etc.) no pueden ser aislados correctamente mediante objetos.
Así que con el objetivo de solucionar esto, se inventó la programación orientada a aspectos, que intenta separar estos aspectos comunes de infraestructura, de la funcionalidad básica.
Separation of concerns
Como ya sabemos, separation of concerns o separación de incumbencias (lo siento, no conozco una linda traducción para concerns) es un principio muy importante en la ingeniería del software.
Este principio, sostiene que en todo sistema, hay ciertos atributos y comportamientos inherentes que deberían ser identificados y separados, llamados concerns o incumbencias.
Estas separaciones de concerns, pueden lograrse mediante diferentes técnicas y construcciones. Por ejemplo, en el paradigma orientado a objetos, se hacen mediante objetos.
Pero ojo, el principio de separation of concerns, no es la mera separación de un sistema en partes. Sino que, involucra la cohesión además de la separación. Es decir que las separaciones no se hacen arbritariamente, ni se basan en el número de líneas del código, ni en la separación de tareas en pasos lo más cortos posibles sin ningún beneficio. Al contrario, se hacen con el objetivo de establecer un orden y una organización en el sistema, que nos brinde ventajas como ser una mejor mantenibilidad, extensibilidad, reusabilidad, etc.
La calidad del software está directamente relacionada con estos factores, dado que son determinantes en la eficiencia, agilidad de desarrollo, etc. Por esta razón, el principio de separation of concerns es clave en la ingeniería del software, ya que establece como deben abstraerse y separarse las partes de un sistema para lograr la mayor cohesión posible y así obtener los beneficios citados.
Separación horizontal
SoC, al ser un principio tan abstracto, puede aplicarse de diferentes maneras. Una de ellas, es separar la aplicación en capas lógicas de funcionalidad que tienen en común que cumplen el mismo rol dentro de la aplicación.
Un caso típico es el de la arquitectura en tres capas, donde se separan claramente la presentación, de la lógica de negocio, de los datos.

Con esta separación, se puede minimizar el nivel de dependencias dentro de la aplicación. Por ejemplo, en el caso de necesitar modificar algo de la interfaz de usuario, sólo debe modificarse la capa de presentación sin alterar las demás.
De esta manera, se incrementa la adaptabilidad y reusabilidad del código, ya que se aisla mejor el impacto al cambio.
Separación vertical
Este tipo de separación, se basa en separar en módulos de funcionalidad que tienen en común cierta feature o caso de uso dentro de la aplicación.

Es útil cuando se tienen varios equipos de desarrollo, se los puede poner a trabajar por separado sobre diferentes módulos del sistema.
Separación multidimensional
Una separación vertical, no anula una separación horizontal, ni al revés tampoco. Es decir, que se pueden utilizar dos tipos de separation of concerns al mismo tiempo. Por ejemplo, podemos dividir una aplicación en módulos y a su vez cada módulo en capas que establezcan el rol de los componentes dentro del módulo.

Separación por aspectos
Hay concerns, que no encajan con ninguna de las dos separaciones (ni como capas en la separación horizontal, ni como módulos en la vertical). Estos concerns, se diseminan por todo el sistema, ya que se integran con todos los demás concerns. Se denominan crosscutting concerns o aspectos.
En la separación por aspectos, se sostiene que en una aplicación se pueden distinguir dos tipos de concerns:
- Componentes: se trata de los típicos concerns. Son módulos de software que pueden ser encapsulados (ya sea mediante un objeto, método, procedimiento, etc.). Son unidades funcionales en las que se puede dividir el sistema. Por ejemplo en una aplicación bancaria los componentes podrían ser: cuenta, cliente, etc.
- Aspectos: pertenecen a un tipo especial de concern, los crosscutting concerns. Estos no se pueden encapsular mediante las construcciones que nos ofrecen los paradigmas actuales. Ya que no son unidades funcionales en las que se pueda descomponer el sistema. Sino que se disgregan por todo el sistema, afectando a todos los componentes. Ejemplos de esto, son la sincronización de threads, la gestión de memoria, el manejo de transacciones, la distribución, etc.
Sabiendo esto, la separación por aspectos intenta desacoplar mediante nuevas abstracciones, los aspectos de los componentes. Esto disminuye la complejidad, brindándonos más mantenibilidad. Además al reducirse el acoplamiento se obtiene más reusabilidad. Y a la hora de agregar componentes, más flexibilidad.

Ventajas de POA
Como ya dijimos, cualquier tipo de SoC intenta lograr una mayor cohesión entre los diferentes concerns (incumbencias) del sistema. Disminuyendo así el acoplamiento entre ellos, y mejorando de esta forma factores importantes del sistema: flexibilidad, mantenibilidad, extensibilidad, reusabilidad, etc. Que como ya dijimos, determinan la calidad del software.
AOP no es más que una aplicación de SoC, así que las ventajas siguen siendo las mismas…
- Mayor nivel de abstracción que permite al desarrollador concentrarse sólo en la lógica de negocio de la aplicación, sin preocuparse por aspectos de infraestructura como la gestión de memoria, threads, etc.
- Menor acoplamiento => mayor reusabilidad.
- Menos complejidad => mayor mantenibilidad.
- Mayor flexibilidad en la integración de componentes.
Como funciona POA
Ahora que entendemos que es POA en teoría, veamos como funciona, como es que se logra desacoplar los aspectos de los componentes del sistema. ¿Cómo hacer para separarlos y que al mismo tiempo puedan integrarse correctamente? bueno ahora vamos a ver todo eso…
Elementos de POA
Primero que nada, hay que aclarar que la programación orientada a aspectos, no anula a la programación orientada a objetos. Sino que se integran como vamos a ver en un momento.
Estos son los elementos que intervienen en el paradigma orientado a aspectos:
- Un lenguaje base (para la funcionalidad básica) como ser C++ o Java.
- Uno o más lenguajes orientados a aspectos (como vamos a ver en un rato).
- Un weaver (o tejedor) que se encarga de “tejer” o integrar, los componentes con los aspectos.
Integración de componentes con aspectos
Como ya dijimos, el weaver es el encargado de entrelazar los componentes con los aspectos.
Para esto, se le especifica una serie de reglas al weaver, que le indican “que” acciones ejecutar “cuando” ciertos punto de ejecución son encontrados. Estas reglas, según la implementación de AOP, se pueden definir de diferentes maneras (con código Java, con annotations o bien con XML).
Entrelazado dinámico y estático
Este entrelazado entre los aspectos y componentes, puede ser llevado acabo de dos maneras:
- Entrelazado estático: se realiza la integración en tiempo de compilación. Esto implica modificar el código fuente de los componentes insertando las sentencias en determinados puntos con el objetivo de integrar los aspectos.
- Entrelazado dinámico: se realiza la integración en tiempo de ejecución, mediante técnicas como la reflexión. Es más costoso en rendimiento que el entrelazado estático.
Lenguajes orientados a aspectos
Mediante los lenguajes orientados a aspectos, se definen en el código fuente los aspectos para que después el weaver los integre en tiempo de ejecución o compilación con los componentes.
Existen dos tipos de lenguajes:
- Específicos: sirven para un determinado tipo de aspecto. Es decir, abstraen y separan un tipo de aspecto de la aplicación. Por ejemplo COOL es un lenguaje orientado a aspectos que sirve nada más para la sincronización de threads. O por ejemplo RIDL es otro lenguaje, pero sirve nada más para los aspectos de distribución. Como pueden ver, en una aplicación puede haber más de un lenguaje orientado a aspectos (podría haber uno para sincronización, otro para distribución, etc.).
- Generales: son de propósito general, sirven para cualquier tipo de aspecto. Pueden personalizarse pero tienen menos nivel de abstracción que los lenguajes específicos, ya que como siempre, la flexibilidad tiene un costo. Ejemplo: AspectJ.
Implementaciones de AOP
Existen muchas implementaciones de AOP, entre ellas las más conocidas son: JBoss AOP, Spring AOP, AspectWerkz y AspectJ.
Básicamente, lo que toda implementación hace, es proveernos de un conjunto de herramientas, como ser un compilador, un debuguer, etc. Para que podamos integrar los aspectos en la aplicación.
Es decir, una implementación de AOP, debe proveernos de:
- Una manera de declarar los aspectos, ya sea mediante código Java, annotations, o XML. Cada implementación usa un método distinto para hacer esto, con diferente sintaxis.
- Un mecanismo para construir los aspectos e integrarlos con los componentes de la aplicación. Cada implementación puede utilizar un mecanismo distinto, por ejemplo algunas lo hacen en tiempo de compilación, otras durante tiempo de ejecución mediante reflexión.
Estas dos cosas (la manera de declarar los aspectos y el mecanismo para construirlos e integrarlos en la aplicación) son las que hay que tener en cuenta a la hora de elegir una implementación de AOP.
AspectJ
Que es AspectJ
AspectJ es una implementación de AOP de aspectos de propósito general, que funciona como una extensión de Java. Consiste fundamentalmente en dos partes:
- La especificación, que define el lenguaje de AspectJ.
- Y la implementación, que nos provee de un weaver/compilador y demás herramientas para debuguear, etc.
Como funciona AspectJ
En tiempo de compilación, se le definen ciertas reglas al weaver/compilador de AspectJ, que le especifican que acciones ejecutar y cuando, es decir, que aspectos y como integrarlos con los componentes.
Para poder definir estas reglas, AspectJ, como cualquier lenguaje orientado a aspectos, nos provee de algunas abstracciones:
- Join points / puntos de enlace: son puntos del código que podrían ser posibles pointcuts. Estos incluyen llamadas a métodos, accesos a miembros de una clase, y la ejecución de bloques de manejo de excepciones.
- Pointcuts / puntos de corte: definen puntos de ejecución del programa, a los cuales se les puede asociar acciones, estos pointcuts no pueden estar en cualquier lugar del código, sino que sólo en los join points.
- Advices / avisos: son las acciones que se pueden asociar a los pointcuts.
- Introducciones: introducen cambios en clases, interfaces, y aspectos del sistema.
- Declaraciones en tiempo de compilación: permiten añadir warnings y errores.
Las reglas se especifican mediante el lenguaje AspectJ, que está basado en la sintaxis de Java y nos ofrece diferentes construcciones para las abstracciones mencionadas.
El weaver lee las reglas y compila las clases, integrando los componentes con los aspectos. Como resultado obtenemos ficheros .class, que pueden ser ejecutados por la JVM normalmente. Estos binarios son programas Java comunes y corrientes pero contienen las instrucciones necesarias para que se integren los aspectos con los componentes en tiempo de ejecución.
Obtener AspectJ
La implementación original de AspectJ fue la de Xerox, pero este después lo liberó bajó licencia open source y se hicieron varias reimplementaciones. Una de ellas es la de Eclipse, que es con la que vamos a trabajar en este artículo.
Podemos obtener la implementación de AspectJ de Eclipse de acá: http://www.eclipse.org/aspectj/
Para los que usan Eclipse, les recomiendo descargarse el plugin AJDT. Es un conjunto de tools que nos simplifican bastante la existencia a la hora de usar AspectJ.
Hello World con AspectJ
Tenemos la siguiente clase Insult. Que tiene dos métodos, show() que escribe un insulto al azar en la salida estandar, y show(String name) que escribe en la salida estandar un insulto personalizado.
[sourcecode language="java"]
package insult;
import java.util.Random;
public class Insult {
private static final String[] insults = {"mother fuc@@@", "sun of a b@@@@"};
private String random() {
return insults[new Random().nextInt(insults.length)];
}
public void show(String name) {
System.out.printf("%s, %s\n", name, random());
}
public void show() {
show("You");
}
public static void main(String[] args) {
Insult insult = new Insult();
insult.show();
}
}
[/sourcecode]
Vamos a crear un aspecto, que antes de la ejecución de cualquiera de los dos métodos, escriba en la salida estandar la fecha y hora actuales:
Así que creamos con AspectJ, un aspecto llamado TimeAspect que tenga un pointcut en la llamada de los métodos de la clase Insult, asociado con un advice que escriba en pantalla la hora actual:
[sourcecode language="java"]
package insult;
import java.util.Date;
public aspect TimeAspect {
pointcut showMethods()
: execution(* show(*));
before() : showMethods() {
System.out.println(new Date());
}
}
[/sourcecode]
Claro que este ejemplo es bastante inútil, ya que escribir la hora y fecha actuales no se trata de un crosscutting concern. Pero es para que se vayan familiarizando con las construciones y sintaxis de AspectJ.
Otro ejemplo con AspectJ
Vamos a construir el sistema de logs de una aplicación utilizando aspectos, con AspectJ.
Primero que nada, ¿por qué usar aspectos para el manejo de logs de una aplicación?
Bueno, lo cierto es que ya existen muchos toolkits para el manejo de logs como Log4j, que simplifican mucho la tarea. Sin embargo, aún con estas herramientas seguimos teniendo que insertar las sentencias de logging por todos lados en el código. Por esta razón, y basándonos en lo explicado anteriormente, podemos llegar a la conclusión de que el manejo de logs es un crosscutting concern, ya que es utilizado por diversos componentes de la aplicación y no se puede separar claramente. Por lo tanto podemos utilizar AOP para aislarlo como un aspecto y así desacoplarlo de los componentes.
Bien, vayamos a nuestro ejemplo… Tenemos un componente Persona, que obviamente está abstraído en una clase, por simplicidad va a ser el único componente de la aplicación:
[sourcecode language="java"]
package test;
import org.apache.log4j.Logger;
public class Person {
private String name;
private int iq;
private static final Logger logger = Logger.getLogger();
public Person(String name, int iq) {
this.name = name;
this.iq = iq;
}
public String getName() {
logger.debug("Entering " + " test.Person.getName()");
return name;
logger.debug("Exiting " + " test.Person.getName()");
}
public void setName(String name) {
logger.debug("Entering " + " test.Person.setName()");
this.name = name;
logger.debug("Exiting " + " test.Person.setName()");
}
public void doSomethingStupid() {
logger.debug("Entering " + " test.Person.doSomethingStupid()");
iq–;
logger.debug("Exiting " + " test.Person.doSomethingStupid()");
}
public static void main(String[] args) {
Person person = new Person("Homer", 80);
person.doSomethingStupid();
}
}
[/sourcecode]
Como se puede ver, el mismo componente se encarga del logging. Pero como ya dijimos, consideramos al logging como un crosscutting concern. Así que vamos a aislarlo como un aspecto mediante AspectJ.
Así que creamos un aspecto llamado TraceAspect. Dentro de este colocamos un pointcut llamado traceMethods, que establezca como pointcuts todas las llamadas a todos los métodos públicos dentro del paquete test de la aplicación. Y asociamos un advice a este pointcut que genere los logs.
[sourcecode language="java"]
package test;
import org.apache.log4j.Logger;
public aspect TraceAspect {
private static final Logger logger = Logger.getLogger();
pointcut traceMethods() :
execution(public * test.*.*(..));
before() : traceMethods() {
System.out.println("Entering " + thisJoinPoint.getSignature());
}
after() : traceMethods() {
System.out.println("Exiting " + thisJoinPoint.getSignature());
}
}
[/sourcecode]
Ahora el componente persona ya no tiene que encargarse del logging:
[sourcecode language="java"]
package test;
public class Person {
private String name;
private int iq;
public Person(String name, int iq) {
this.name = name;
this.iq = iq;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void doSomethingStupid() {
iq–;
}
public static void main(String[] args) {
Person person = new Person("Homer", 80);
person.doSomethingStupid();
}
}
[/sourcecode]
Bueno, hay muchos problemas con este ejemplo, pero como ya dije antes, no vamos a detenernos en detalles en este artículo. En próximos artículos vamos a ver esto en más profundidad. El punto era por ahora poder dar una idea inicial de AOP.
AOP en EJB
A partir de EJB 3, se introdujo el concepto de interceptors en la especificación, que nos permiten utilizar AOP en los EJB. Pero este es un tema para otro artículo también, por lo que sólo lo digo como un comentario.
Conclusiones
AOP es un paradigma muy reciente, y como todo, tiene su curva de aprendizaje, por lo que probablemente pase un tiempo hasta que se logre exprimir su poder al máximo. Pero sin duda es algo que vale la pena conocer, ya que puede llegar a marcar una nueva manera de diseñar y escribir el software.
18 comentarios18 comentarios
Dejá una respuesta








Parece util,sobre todo si como dices ,ademas de pointcut a metodos (para controlar el flujo del programa , por donde pasa y quizas hacer profiling con timestamps), tambien deja hacerlos automaticamente a la parte de manejo de excepciones ,para dar mayor informacion de las mismas y generar ,p.ej. una alerta del sistema.
Quizas un ejemplo de Join points / puntos de enlace manejando excepciones seria aclarador.
No tenia idea de este paradigma de programación, pr l oque se ve es bastante útil, espero el 2° articulo con más ejemplos ;)!
quiza concerns puede traducirse por aspectos no?
Me gusta mucho el blog
saludos!
En realidad “cross-cutting concern” se traduciría como aspecto… “concern” podría traducirse como concepto, incumbencia, obligación, responsabilidad, interés, ocupación, algunos lo traducen como preocupación… pero la verdad es que si uno anda por ahí diciendo “separación de preocupaciones” o similares la gente va a mirarte raro… me quedo con la palabra en inglés como en la mayoría de los casos.
sorprendido por que es la conclusion a la que llegaba cuando mis profes me decia que la poo era el balsamo sagrado que lo curaba todo -y yo era el unico idiota que preguntaba…y los procesos??-
sorprendido por que no sabia que ya esta armado un paradigma mas o menos robusto
sorprendido por que me ha sorprendido una mujer
felicitaciones
He estado en varias charlas acerca del tema y este post no puede ser mas claro y conciso.
Ya conocía el POA, pero te felicito porque es una de las mejores explicaciones que he visto, al menos en español.
Espero impaciente la segunda parte del artículo.
Pues yo ya lo utilizaba sin usar pointcuts jejeje…Claro que no sabía que estaba programando aspectos (lo hago con java pero todo con clases, las cuales las aíslo mucho y me las ingenio para aislar los componentes que afectan toda la app)…es bueno saber hacer como se debe y no como se te ocurre.
Saludos sherekan.
mnmm, varios cosas a decir…
si un programador delega en un aspecto un problema de sincronizacion lo mato mismo el log del mismo. Tal vez algo medio complejo para medir tiempo de txs en un metodo, podriaaaa poner un aspecto, pero delegar todo: imposible.
No lei todo lo demas, pero hay cosas mas piolas que aspectJ en java x ejemplo… pero no es tan AOP ya que entra en la parte de weaving (vease: javassist, aop aliance, etc).
Pero we, el articulo esta bien para gente que no tiene idea del tema xD
P.S: AOP no es para nada nuevo xD… ya lleva un tiempo bastante largo =)
Sabía que tenía que existir algo así xDD
Yo también había observado los problemas del POO al enfrentarse a según que aspectos. Lo que no sabía que existia una forma tan simple de eliminarlos, le veo futuro al POA, no le quitaré el ojo de encima.
Al igual que el POO respecto a los viejos lenguajes estructurales, el POA es una continuación lógica de su predecesor, por eso hay gente que a llegado a trabajar con POA sin ser consciente.
Espero siguientes artículos para aclarar mas el concepto, sobretodo los poincut, que son quizás un poco mas complejos.
Saludos
Muy buen artículo
Excelente articulo, tienes una forma muy sencilla de explicar las cosas y logras que el entendimiento sea rapido y no frustrante.
No tenia idea acerca de este nuevo paradigma para la programacion, estoy ansioso por ver tu segundo articulo. Infinitas gracias desde Chile.
En el siguiente artículo sobre POA (que espero que sea pronto) puedes comentar algunos ejemplos prácticos? En la página de AspectJ (http://www.eclipse.org/aspectj/doc/released/progguide/examples.html) algunos hay ejemplos curiosos pero no me convencen. Gracias!
mis felicitaciones por explicar conceptos (más o menos) complejos de una manera tan didáctica!
gracias, y saludos…
Muy bien explicado! GRACIAS!
PAZ!!
estaba buscando info por la web por que estoy coordinando una compañia de arte que trabaja con intervenciones interdiciplinarias, musica, danza, cine, teatro,intervenciones sonoras, multimedia, etc, estamos trabajando con max/msp para intervenir obras de arte y asi llegué aquí, en fin, gracias por la explicacion sencilla que das, me ha permitido entender un poco sobre el tema, cosa que se hace muy dificil cuando no se conoce a fondo el lenguaje tecnico. trabajamos en buenos aires, proximamente presentaremos unos trabajos en la plata, cualquier persona que se interese en el tema sera bienvenido para participar. gracias!
Excelente explicación, como introducción a POA es muy clara e incluso incluye ejemplos prácticos.
Te felicito es un buen trabajo.