Buscar en este blog....

Mostrando entradas con la etiqueta POA. Mostrar todas las entradas
Mostrando entradas con la etiqueta POA. Mostrar todas las entradas

domingo, 24 de julio de 2016

Writing your first Aspect with Postsharp in C#

Hi!

As some of you might remember, some years ago I started to dabble in the world of Aspects Oriented Programming. I also wrote three posts explaining what is aspect oriented programming, what are its advantages and I gave an example on how to implement aspects in Java using AspectJ. That was a few years ago. Nevertheless, those posts are more relevant than ever!! So if you don't feel that sure with AOP, I´d recommend you to read a few on what aspects are to understand it before you continuing reading.

But life and likes started taking me to and through the .NET and C# world, so I began to look for new tools. Today there are really a lot of tools and frameworks to support aspects, however I chose the one called Postsharp.

Until now, I must admit that the simplicity of this framework seems wonderful for me when compared with other like Spring.NET AOP, which requires a little more of skills when setting it up. Postsharp doesn't need to be configured!! So this is the first advantage.

That said, let's go to the point and see an example on how this framework works.

Maybe the correct thing would be to start defining some concepts that I didn't clarify in those posts, and which are usefull to understand what we are doing. But no: today we start directly with an example. After that, in other posts, I will clarify these points.


So, lets imagine a very simple scenery. It's about a well-known and complex application called "Hello, World" ;), on which we are going to add an aspect. We are going to create only one aspect, that will be responsible to intercept the program's execution just before calling the method who writes "Hello World" in to the screen.

Step 1: Add Postsharp to the project.

Once we've created an empty project (e.g. Console Application), we add the Postsharp packet to it. The simplest way to do it is, of course, using NuGet. So easy as opening NuGet and searching the framework by its name. Then adding it to the project.

Here it is worth it to mention, that once added to the project, Postsharp asks the user to download an executable to be installed (version VS > 2010). This is meant to install a component that will added to Visual Studio. Postsharp has a free version which is enough to write aspects with total freedom, but it also has two more versions which are not only not free, but very expensive for the single user, but provide already-written and already-working aspects and also the possibility to write complex aspects. I haven't had yet the chance to try them, but I'm gonna do it as soon as possible. But do not be afraid of it: get the Express version (the free one) to continue. This installation needs to be made only once. So its not a problem.

Step 2: Write the "Hello World".

Actually, steps 1 and 2 are interchangeables. It is really the same. We can write first the "base" project (without aspects) and then install Postsharp.

I know it can be difficult, so I'm gonna help you. To create the "Hello World" project, you should call the method WriteLine() to write in the console, in the main class.

using System;

namespace FirstTryWithPostSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
        }
    }
}

I guess you didn't have much more complications in this step! ;) Let's move on!

Step 3: Write the Aspect.

Now the interesting part: an aspect, is nothing else but a class! Yes, that simple! Well.. but actually this class must accomplish two conditions:

  1. It must be ALWAYS serializable. This is achieved applying the "Serializable" attribute as we'll see soon..
  2. It ALWAYS inherits from one Postsharp's class, which give our class the "super powers", to put it in some way. They aren't actually superpowers, but these Postsharp's classes have methods which can be overridento redefine them. Postsharp's Core will insert them in our code during compile time. In our example, it will be inserted just before the call to Main() method.
Ok, lets create the class, which we can call AspectThatInterceptsAMethod and says this:



using PostSharp.Aspects;
using System;

namespace FirstTryWithPostSharp
{
    [Serializable]
    class AspectThatInterceptsAMethod : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionArgs args)
        {
            Console.WriteLine("Entering the method: " + args.Method.Name);
        }
    }
}


Briefly I want to say that I assume that we already know where this class is being indicated as Serializable, and that the aspect (the class) inherits from the class  OnMethodBoundaryAspect, which provides the possibility to rewrite some methods. In this case, we chose the method OnEntry() which indicates that whatever we do inside it will be executed just before executing the methods to which this aspect will be applied. But like OnEntry(), there are other methods that you can also redifne: OnExit(), OnSuccess() y OnException(). Lets see how to apply this aspect to the Main() Method.

Another little detail, is that args.Method.Name give us the name of the method that was intercepted. This, and much, much more (like accesing to the parameters, and its values) are part the powerful tools with which we count.

Step 4: Indicate to which methods our aspect will be applied.

So far we've made an aspect as god demands. But it is isolated, i.e. it is not being used! To use it, we just have to apply it as an attribute in the methods we want to be affected by it. This is made incredible easy in the following way:

 [AspectThatInterceptsAMethod]
        static void Main(string[] args)
        {


If we now run the project, we'll see that the aspect's text is showed at first, and then Main method's text. This occurs this way, because the aspect will be executed always before the method. Why? Just because we decided to overwrite the OnEntry() method. If we'd wanted to show the text before AND after the main's method, then we should have overwritten also the OnExit() method inside the aspect. Yes, only that!.

Another interesting tip is, that if we want the aspect to be applied to ALL methods of a class, it's just needed to put the attribute over the class name. Like this:

    [AspectThatInterceptsAMethod]
    class Program
    {

        static void Main(string[] args)
        {


And in this way, any method will be automatically a victim of our aspect ;) Easy, don't?

I invite you to try it by yourself!

This was an introduction to a very simple aspect. The aspects's world is very much extensive and interesting! Many wonderful things can be done but, however, like everything in software, I recommend you: always analyze which are the real advantages and disadvantages of introducing a new practice, framework or whatever. Aspects are not always needed. Remember: "Having a good hammer doesn't means that every problem is a nail".

In my experience, the use of aspects is a growing trend, but because many people is getting used to it and not being afraid of it. I'll try to come back with new things about Postsharp's aspects.

viernes, 31 de julio de 2015

Aspectos en C# con Postsharp (Un ejemplo muy simple)

Buenas!
Como algunos recordarán, hace unos años comencé a incursionar en el mundo de la Programación Orientada a Aspectos. En aquella época, hice tres entradas explicando qué es la programación orientada a aspectos, qué ventajas tiene, y hasta di un ejemplo de cómo implementarla en Java usando AspectJ. De eso hae ya unos años. Sin embargo, dichas entradas siguen más vigentes que nunca!! (Así que si no sabés muy bien lo que son y querés una introducción corta y clara, te recomiendo las dos primeras antes de seguir leyendo)

Sin embargo, la vida y los gustos me fueron llevando al mundo de .NET y C#, por lo que comencé a buscar nuevas herramientas. Hoy en día, hay realmente muchas herramientas y framworks para trabajar con aspectos. Sin embargo, yo preferí inclinarme al framework Postsharp.

Hasta ahora, admito me parece una maravilla la simplicidad de este framework comparado con otros, como Spring.NET AOP, que requieren un poquito más de maña con la configuración. Postsharp no necesita configurarse!!

Bueno, vamos al grano, y veamos un ejemplo de como funciona este sencillo framework.

Quizás lo correcto sería comenzar con algunas definiciones que no aclaré en los posts pasados, y que son de utilidad conceptual. Pero no: hoy comenzamos con un ejemplo, directamente. Después, en otro post aclararemos esos puntos.

Muy bien: vamos a suponer un escenario muy simple. Se trata de una compleja aplicación conocida como "Hola Mundo", a la cual le vamos a añadir un aspecto. Vamos a crear un solo aspecto que se encargue de interceptar la ejecución justo antes de ejecutar el método que escribe "Hola Mundo".

Paso 1: Agregar Postsharp al proyecto.

Una vez creado un proyecto vacío (por ejemplo, una consola), le agregamos el paquete de Postsharp. La manea más simple de hacerlo es, naturalmente, con NuGet. Tan simple como abrir NuGet y buscar el framework por su nombre. Luego agregarlo al proyecto.
Aquí vale aclarar, que una vez agregado al proyecto, Postsharp pide al usuario de descargar un ejecutable e instalarlo. (version VS > 2010). Esto es para instalar un componente agregado al Visual Studio. Postsharp tiene una versión gratuita que es suficiente para escribir aspectos con total libertad, pero también posee dos opciones más (que son pagas, y muy caras) las cuales proveen aspectos ya escritos y funcionando. Personalmente no he tenido el agrado de probar estas versiones aún. Pero sin miedo: instalá la versión Express (gratuita) para poder seguir. Esta instalación solo se realiza una vez.

Paso 2: Crear el Hola Mundo.

En realidad, los pasos 1 y 2 son intercambiables. Realmente es lo mismo. Podemos hacer primero el proyecto "base" (sin aspectos) y luego instalar Postsharp.

Para crear el proyecto "Hola Mundo", nada tan simple como en la clase principal hacer una llamada a la consola para escribir en pantalla

using System;

namespace PruebaPostSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hola Mundo!");
        }
    }
}


Este paso fue  fácil, no? ;) Sigamos!

Paso 3: Crear el Aspecto.

El aspecto es, nada más y nada menos, que una clase. Si así de simple. Bueno, es una clase con dos particularidades:

  1. SIEMPRE debe ser serializable. Esto se logra aplicando el atributo "Serializable" como ya veremos.
  2. SIEMPRE hereda de alguna clase padre proveniente de Postsharp, la cual le da los "poderes" de aspecto, por decirlo de alguna manera. En realidad, no son poderes, sino son clases que le permiten redefinir métodos (override) que ya tienen y que luego el core de Postsharp va ubicar en nuestro código. (Para nuestro ejemplo, lo ubicaría justo antes del llamado a "Main()".

Creemos una nueva clase, que se llame AspectoQueInterceptaUnMetodo y dice así:

using PostSharp.Aspects;
using System;

namespace PruebaPostSharp
{
    [Serializable]
    class AspectoQueInterceptaUnMetodo : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionArgs args)
        {
            Console.WriteLine("Entrado al método: " + args.Method.Name);
        }
    }
}

Brevemente quiero dar por sentado que ya sabemos donde esta indicado que la clase es serializable, y que el método hereda de la clase OnMethodBoundaryAspect, la cual le provee la posibilidad de reescribir algunos métodos. En este caso, elegimos el método OnEntry() que indica que lo que haga ese método se hará justo antes de ejecutar los métodos a los que se les aplica ese aspecto. Pero también hay otros: OnExit(), OnSuccess() y OnException(). Veamoslo en acción.

Otro detalle, es que en args.Method.Name tenemos almacenado el nombre del método que fue interceptado antes de llamar al aspecto. Esto, y mucho más (como acceso a los parametros, y los valores de los parámetros) son poderosas herramientas con las que podemos contar.
 

4) Indicar qué metodos serán afectados por el aspecto.

Hasta aquí tenemos un aspecto hecho y derecho. Pero está aislado: es decir, nadie lo usa. Para usarlo, solo debemos definirlo como atributo de el/los métodos a los que queremos que afecte.

De la siguiente manera:

 [AspectoQueInterceptaUnMetodo]
        static void Main(string[] args)
        {


Si ahora ejecutamos el proyecto, vemos que se muestra primero el texto del aspecto, y luego el del método Main. Esto es porque el aspecto siempre se ejecuta antes que el método. Si quisieramos que se ejecute después, deberíamos sobre escribir el método OnExit(). Si queremos que se ejecute antes y después, deberíamos sobreescribir ambos métodos: OnEntry() y OnExit(), y asi de simple funciona esto.

Otro tip interesante, es que si queremos que el aspecto se aplique a TODOS los métodos de una clase, solo basta con colocar el atriuto sobre la clase en cuestión:

    [AspectoQueInterceptaUnMetodo]
    class Program
    {

        static void Main(string[] args)
        {


Y de esta forma, cualquier método automáticamente será víctima de nuestro aspecto!! Simple, no?

Te invito a que pruebes vos mismo!

Esto fue una sencilla introducción a un aspecto muy simple. El mundo de aspectos es mucho más grande e interesante! Se pueden hacer cosas realmente estupendas pero, sin embargo, como todo en el software, siempre hay que analizar las ventajas y soluciones reales que esta (o cualquier otra) práctica puede conllevar. No siempre se necesitan aspectos.

En mi experiencia, el uso de aspectos es una tendencia que está creciendo, porque mucha gente está animándose a desarrollar con aspectos cada vez más gracias a los frameworks como postsharp, Spring.NET, etc Y si bien aún no es tan común, y aún no se lo práctica de la mejor forma, esto está cambiando de forma veloz, como todo en este campo.

Próximamente iremos mostrando cosas nuevas, más interesantes y más avanzadas!

Saludos!

viernes, 22 de abril de 2011

Programación Orientada a Aspectos. Implementando un Aspecto. (Parte 3)

En la parte 2 planteamos una situación utópica: que una clase realizara algo sin indicarle que lo hiciera. Bueno, sí, eso es utópico. Pero, podemos pensar en otra "entidad" que se encargue de realizar esa tarea (en nuestro caso es registrar un movimiento) detectando el momento preciso en el que debe realizarla. Es decir, tener "algo" que sepa cuando registrar un movimiento, pero que ese "algo" no tenga nada que ver con la clase Cuenta. Ese algo, es lo que llamamos aspecto. Un aspecto, entonces, es algo que entrecruza una o más clases. Dicho de otra forma, un aspecto es un concepto que ofrece funcionalidad transversal a los objetos del negocio.

Imaginémoslo como algo que vigila un programa a medida que este se ejecuta; y a partir de ciertas acciones que se producen en el programa, este algo realiza acciones propias de él. Llamemos a este algo "aspecto". Podemos decirle a un aspecto, que cuando se termine de ejecutar cualquier método que comience con la palabra "hacer" realice un registro de movimiento. La clase Cuenta de nuestro ejemplo, no se enteraría jamás de la existencia de este aspecto, pero este aspecto está atento a las ejecuciones hacerAlgo...() de esa clase.

Vamos a definir rápidamente este aspecto para poder visualizar mejor lo que estamos tratando de decir. Para poder escribir un aspecto y compilar el programa con este aspecto, es necesario tener instalado el plugin de AspectJ en el editor de Java que estemos usando, o poder compilar configurando el editor. Bueno, pasemos a definir el aspecto de una vez. Agregamos un nuevo archivo con extensión .aj (por "AspectJ") al proyecto:

public aspect RegistroDeMovimientos {

after():execution (public * hacer*(..)) {
Date hora = new Date();
SimpleDateFormat formatoDeFecha = new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss");
System.out.println("Movimiento realizado a las " + formatoDeFecha.format(hora)) ;
}
}


Observemos este código. Lo primero que vemos, es la declaración del aspecto bajo el nombre de RegistroDeMovimientos. Por ende, al igual que las clases de Java, el archivo del aspecto debe llamarse RegistroDeMovimientos.aj. Si seguimos vemos la linea que dice:

after():execution (public * hacer*(..)) {


Esta línea es clave para definir unos conceptos que explicaré más adelante. Pero por el momento, analizaremos de manera simple qué significa. Si tuviera que decirlo en lenguaje informal, la describiría asi: "Después de la ejecución de cualquier método público, sin importar el tipo que retorne, que comience con hacer, y sin importar la cantidad y tipo de parámetros que requiera, hacer lo siguiente:". Eso significaría mas o menos. Ahora examinemos lo que significa cada parte:

  • after(): Esto indica que el cuerpo se ejecutará después de algo. Ahora veamos ese algo.
  • execution ( ): esto significa que se toma como referencia una ejecución de lo que sigue:
  • public * hacer*(..): public nos indica que se buscará un método público cualquiera. El asterisco que sigue, indica que ademas no importa el tipo que retorna el método. hacer* significa que el nombre del método debe comenzar con la palabra "hacer" y no importa cómo termine (para eso está el asterisco luego de "hacer"). Y finalmente, los dos puntos entre paréntesis significa que no importa ni el tipo ni la cantidad de parámetros del método.

Entonces, revisemos la descripción informal del método: "Después dela ejecución de cualquier método que sea público, que retorne cualquier tipo de dato, que comience con "hacer" y sin importar el tipo y cantidad de parámetros", se debe ejecutar lo que se indica en el cuerpo. El cuerpo es muy simple: no es mas que aquello que hacía el método RegistrarMovimiento() que hicimos antes. Esto es un aspecto. Simple, ¿no? El aspecto, se encargará de ejecutar ese cuerpo de código cada vez que ocurra esa descripción informal.

Con esto, hemos solucionado el problema del apartado anterior. ¿Por qué? Porque hemos logrado aislar el comportamiento de registrar un movimiento, de forma tal que la clase Cuenta no conoce de su existencia. Por lo tanto, no hemos tenido que repetir ningún código dentro de la clase, y además, al aislar ese comportamiento en un sólo aspecto, este se hace más fácilmente mantenible, ya que si queremos modificar la forma en que se registran los movimientos sólo debemos modificar el cuerpo de ejecución del aspecto, y no preocuparnos por las clases del negocio.

Por supuesto, para compilar aspectos, no podemos usar el compilador de java, para eso usamos el compilador AspectJ que lo que hace es armar un código complementando el código base con el código de aspectos. Una vez que esta herramienta genera el código final, ahí si se puede correr sobre la máquina virtual de java.

miércoles, 20 de abril de 2011

Programación Orientada a Aspectos. ¿Por qué usar POA? (Parte 2)

En la parte 1, di una introducción a lo que es la programación orientada a aspectos de una forma bastante teórica. En esta segunda parte, voy a mostrar un ejemplo práctico muy simplificado para poder asentar un poco más el concepto. Para el ejemplo utilicé el lenguaje Java, y la extensión del lenguaje Java que implementa aspectos AspectJ. Ya he contado como compilar con AspectJ en NetBeans.

Bueno, ¿en qué consiste la POA? Voy a poner un ejemplo muy fácil que ilustra el potencial que nos ofrece la POA Supongamos una aplicación de transacciones bancarias. (Este es el típico ejemplo que se menciona en todos lados, y lo voy a poner en mi versión porque es muy ilustrativo).

Vamos a tener una clase cuyo nombre es Cuenta y es una versión muy simplificada de una cuenta de un banco. Esta clase, tiene un atributo llamado saldo de tipo Double que indica el saldo en la cuenta. Y vamos a tener tres métodos: Uno que llamado hacerDeposito() con un único parámetro de tipo Double que indica la cantidad a depositar en la cuenta. Otro llamado hacerExtraccion() que cuenta con un solo parámetro que indica la cantidad de dinero que se quiere extraer de la cuenta. El método restante se llama hacerTransferencia() y tiene dos parámetros: uno indica la cantidad de dinero y el otro es la cuenta a la que se quiere hacer la transferencia. Además tendremos una típica -y muy simplificada- clase que representa un cliente del supuesto "banco" llamada Cliente. Para seguir simplificando (cosa que se me está haciendo mala costumbre ya) un cliente sólo puede tener una cuenta. Ilustremos con UML:



A continuación, les muestro un ejemplo en java bien simple. Lo que haré en el método Main() será crear dos clientes (Pepe y Juan) y crear una cuenta para cada uno. Después, le asignaré a Pepe una cuenta y a Juan la otra. Finalmente, haré una extracción de la cuenta de Pepe; y -felizmente para Juan- terminaré con una transferencia de dinero de la cuenta de Pepe a la de Juan. Aunque lo que realiza el método Main(), por el momento no nos interesa. Veamos el código de ambas clases (sin los getter y los setters porque son muy triviales).

Veamos la definición de los métodos hacer...() de la clase Cuenta:

public void hacerDeposito(Double cantidad) {
 if(cantidad <= 0) {
     System.out.println("No hay dinero suficiente para hacer el depósito");
 }
 else {         
     Date hora = new Date();         
     SimpleDateFormat formatoDeFecha = new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss");         
     //Realizamos el depósito
     this.saldo += cantidad;         
     //Registramos el movmiento
     System.out.println("Movimiento realizado a las " + formatoDeFecha.format(hora)) ;         
 }
}

public void hacerTransferencia(Double cantidad, Cuenta cuentaDestino)
{
 if(this.saldo < cantidad) {
     System.out.println("No hay fondos suficientes para la transferencia.");         
 }
 else {
     Date hora = new Date();         
     SimpleDateFormat formatoDeFecha = new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss");        
     cuentaDestino.hacerDeposito(cantidad);
     this.saldo -= cantidad;
     //Registramos el movmiento
     System.out.println("Movimiento realizado a las " + formatoDeFecha.format(hora)) ;
 }
}

public void hacerExtraccion(Double cantidad) {

 if(this.saldo < cantidad) {
     System.out.println("No hay fondos suficientes para la extracción.");         
 }
 else {         
     Date hora = new Date();         
     SimpleDateFormat formatoDeFecha = new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss");         
     //Extraemos la cantidad del saldode la cuenta.
     this.saldo -= cantidad;         
     //Registramos el movmiento
     System.out.println("Movimiento realizado a las " + formatoDeFecha.format(hora)) ;
 }
}
Examinemos un poco el código: en todos métodos comenzamos con una verificación de la cantidad que se quiere extraer, depositar o transferir. Esta verificación es necesaria, ya que ciertamente sería un error, por ejemplo, depositar una cantidad negativa de dinero de una cuenta; también sería un error permitir una extracción de más cantidad de dinero de la que se dispone. Estas situaciones deben evitarse si o si.

Por otra parte, vemos que una vez que se comprueban las cantidades y que esta todo bien, se procede a realizar el deposito o la extracción o la transferencia (según sea el caso). Pero para esto, he supuesto (a modo de regla de negocio) que el banco necesita tener un registro (logging) de cada movimiento realizado en cada cuenta. Por eso es que creé una variable llamada hora y la mostré en pantalla. Lo más común no es mostrarlo en pantalla sino almacenarlo en un archivo de logs, pero lo hice así por practicidad.

Lo interesante, curioso, y que tenemos que tener en cuenta de estos métodos, es que los tres repiten el proceso de registrar un movimiento. Y no solo eso, sino que además se hace en los tres métodos de la misma forma! Lo cual es una muy mala práctica. Nunca debería repetirse código, eso lo sabemos bien. Entonces, he aquí un problema. ¿Cómo lo solucionamos?...

Algún iluminado podría indicar hacer lo siguiente: Creamos un cuarto método dentro de la clase al que podríamos llamar registrarMovimiento(). Este método se encargaría sólo de la parte del registro del movimiento; y cada uno de los anteriores tres métodos lo llama cuando ha concluido un movimiento. Genial. Veamos cómo quedaría registrarMovimiento():

private void registrarMovimiento()
{
 Date hora = new Date();         
 SimpleDateFormat formatoDeFecha = new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss");         
 System.out.println("Movimiento realizado a las " + formatoDeFecha.format(hora)) ;
}

Y ahora, el código de los otros tres métodos se modifica para llamar a registrarMovimiento() quedando así (sólo mostramos dos, pero el tercero seguiría la misma lógica que éstos):

public void hacerTransferencia(Double cantidad, Cuenta cuentaDestino)
{
 if(this.saldo < cantidad) {
     System.out.println("No hay fondos suficientes para la transferencia.");         
 }
 else {
     cuentaDestino.hacerDeposito(cantidad);
     this.saldo -= cantidad;
     //Registramos el movmiento
     registrarMovimiento();
 }
}

public void hacerExtraccion(Double cantidad) {

 if(this.saldo < cantidad) {
     System.out.println("No hay fondos suficientes para la extracción.");         
 }
 else {
     //Extraemos la cantidad del saldode la cuenta.
     this.saldo -= cantidad;         
     //Registramos el movmiento
     registrarMovimiento();         
 }
}
Pero lo malo de esta solución, es que si se efectuara otro movimiento que no fuera "dentro" de la clase Cuenta sino "dentro" de otra clase, esta clase tendría que tener su propio método registrarMovimiento(), y nuevamente estaríamos repitiendo código, pero entre clases. Ante este nuevo problema, podemos proponer otra solución: creamos una clase que se encargue específicamente de registrar movimientos; y cualquier método de cualquier clase puede instanciarla y llamar al método que registra movimientos. Podríamos, por ejemplo, crear una clase llamada RegistroDeMovimientos con un método público llamado registrarMovimiento(). Parece que esta es la mejor solución. Sin embargo no es así. Sigue habiendo "algo malo". ¿Qué es?...

Está claro: aunque tengamos una clase aparte que registre los movimientos, cada clase debe instanciar y llamar al método de esa clase. Es decir, seguimos repitiendo código. Cada método que deba registrar un movimiento, debe repetir el mismo código que los otros métodos que también deban registrar un movimiento. ¡Que feo! ¡Y no solo eso! Además, el hecho de registrar un movimiento -ya sea dentro del mismo método o llamando a RegistroDeMovimientos.registrarMovimiento()- significa introducir código que no pertenece exclusivamente al movimiento. El movimiento sólo debería realizar el movimiento y nada más. El registro debería hacerse aparte, no debería ser responsabilidad de un depósito, o una extracción o una transferencia. Esta forma de registrar un movimiento, ensucia el código que se refiere a los métodos de la clase Cuenta. Es decir, lo ideal sería que estos métodos se vieran así:

public void hacerDeposito(Double cantidad) {
 if(cantidad <= 0) {
     System.out.println("No hay dinero suficiente para hacer el depósito");
 }
 else { 
     this.saldo += cantidad;
 }
}

public void hacerTransferencia(Double cantidad, Cuenta cuentaDestino)
{
 if(this.saldo < cantidad) {
     System.out.println("No hay fondos suficientes para la transferencia.");         
 }
 else {
     cuentaDestino.hacerDeposito(cantidad);
     this.saldo -= cantidad;
 }
}

public void hacerExtraccion(Double cantidad) {

 if(this.saldo < cantidad) {
     System.out.println("No hay fondos suficientes para la extracción.");         
 }
 else {
     this.saldo -= cantidad;
 }
}
Como dije, sería ideal que a pesar de no llamar a registrarMevimiento(), el movimiento se registrara igual. Eso sería ideal, ¿no? No ensuciaríamos código y tampoco lo repetiríamos. La clase sería tremendamente cohesiva. Parece utópico ¿verdad? Bueno, para nuestra felicidad, esto es realizable y es acá donde entran en juego los "aspectos".

En la tercer parte, voy a mostrar cómo implementar un aspecto que solucione el problema planteado.

viernes, 15 de abril de 2011

Programación Orientada a Aspectos. ¿Qué es? (Parte 1)

Para explicar la programación orientada a aspectos, quizás lo ideal sería comenzar con una breve descripción de la historia de la ingeniería de software, como lo hacen la mayoría de los artículos o PDFs que podemos encontrar en la web, pero como justamente la mayoría lo hace así, yo no. Quien quiera verlo así, busque en la web el tema y ahí van a tener mucho para leer (y no está demás). Pero yo prefiero comenzar yendo al grano.

La programación orientada a objetos trata la manera de modularizar "conceptos comunes" de software. Sin embargo, existen otros conceptos o asuntos que se extienden a lo largo de estos "conceptos comunes" y que no pueden ser modularizados con el diseño orientado a objetos. En un intento más de subsanar lo que la orientación a objetos no puede, aparece una nueva solución: la Programación Orientada a Aspectos (y su consecuente Diseño Orientado a Aspectos).

La programación orientada a aspectos se ha planteado como un nuevo paradigma de programación (con lo que no estoy de acuerdo), el cual consiste en separar los conceptos que entrecruzan varias clases y se extienden a lo largo de éstas, pero que no pertenecen a esas clase en sí mismas. Incluso, los conceptos pueden aparecer no sólo en varias clases, sino de una sola. Ahora la pregunta: ¿de qué tipo de conceptos hablamos? Pensemos como ejemplo, en una aplicación que requiere del registro (logging) de las acciones que realiza. Bien, estos registros "alguien" tiene que llevarlos a cabo, y es normal cargar a muchas clases con la responsabilidad de hacerlo cuando, seguramente, no sea la función principal de esas clases hacer dicha tarea. Como vemos, se trata de una misma funcionalidad (registrar acciones) que se encuentra entrecruzada en varias clases, pero que a la vez no es parte de ninguna.

Otro ejemplo, simple de entender, es el manejo de errores. Los errores hay que tratarlos sí o si en una aplicación. Pero, desde un punto de vista "purista" de objetos, no tenemos clases que traten los errores por sí solos. Siempre las clases que definen la funcionalidad de la aplicación van a tener que lidiar con los errores, y esa no es su función principal. En cierta forma, esto hace a dichas clases menos cohesivas. Un tercer ejemplo puede ser el manejo de la sincronización. Nuevamente, los objetos del negocio deben tratar la sincronización además de su función principal.

En todo los ejemplos estamos viendo que hay ciertos asuntos que no pertenecen al negocio pero que sin embargo, deben ser tratados. Y lo negativo de esto, es que según el paradigma de objetos, son los objetos del negocio quienes deben tratar estos asuntos y, como si fura poco que hagan tareas que no les corresponden, estas tareas se esparcen a lo largo de toda la aplicación, llevando a dispersar código y enredarlo por la misma (a esto último se llama Code Tangling y Code Scattering).

Entonces, para darle más sentido a la POA, veamos en qué se afecta el desarrollo de aplicaciones cuando surgen estos asuntos:
  • Baja cohesión: las clases realizan tareas que no le corresponden específicamente a ellas.
  • Baja reusabilidad: Cuando una clase trata un asunto que no le corresponde, esta clase probablemente sea difícil de re usar en otro sistema.
  • Peor calidad de código: El código se complica, y cuesta más entenderlo y mantenerlo.
  • Evolución dificultosa: Se vuelve más costoso que el sistema evoluciones al tener código disperso y enredado por toda la aplicación.

Estos asuntos son denominados "conceptos transversales". La POA viene para tratar de separar y encapsularlos en una aplicación y aislarlos para independizar a los objetos del negocio de éstos conceptos. Este aislamiento se encapsula en "aspectos".

Por lo tanto, la POA permite la separación de conceptos transversales a través de mecanismos que permiten abstraer y encapsular estos conceptos en un sistema. Un aspecto es la unidad que encapsula uno o más conceptos transversales, y que con la programación orientada a objetos no es posible diferenciarlo de forma clara.

La definición actual de aspecto dice: "Un aspecto es una unidad modular que se disemina por la estructura de otras unidades funcionales. Los aspectos existen tanto en la etapa de diseño como en la de implementación. Un aspecto de diseño es una unidad modular del diseño que se entremezcla en la estructura de otras partes del diseño. Un aspecto de programa o de código es una unidad modular del programa que aparece en otras unidades modulares del programa.

La principal ventaja de la POA es que permite tratar la funcionalidad pura por un lado, y los aspectos por otro, de forma separada. Luego ambos se combinan con un tipo de programa llamado 'Weaver') para dar por resultado el sistema final.

Hay mucho más por hablar de POA aún. En la próxima parte (parte 2), voy a mostrar un ejemplo muy claro, para asentar toda esta teoría en algo más palpable, así que a no abandonar si esta pequeña introducción a resultado medio difícil de entender, en el ejemplo de la Parte 2 quedará muy claro de que se trata.

jueves, 7 de abril de 2011

Instalar AspectJ en NetBeans

UPDATE 1: Para compilar AspectJ en NB 6.9.1, leer esta entrada.

Bueno, este es un punto que me tenía un poco molesto porque no encontraba una buena página que describiera cómo hacerlo. Resultó mucho más fácil de lo que imaginé. La instalación no es precisamente difícil, pero creo que nunca está demás mostrar como hace, sobre todo porque lamentablemente el plugin de AspectJ para NetBeans dejó de desarrollarse oficialmente. Ramón Ramos hizo una nueva versión compatible con está versión del IDE, y es ésta versión del plugin la que instalaremos. Bien, vamos:


1º) Instalar el IDE NetBeans 6.1. Probé de instalarlo en Netbeans 6.9.1 pero no me reconocía algunas partes del plugin, y no las instalaba por un problema de versiones. Como no soy muy ducho con esos temas, opté por directamente instalarlo en la versión 6.1. Ahora, si alguien puede instalarlo en una versión más reciente, que comente y lo agregamos a este pequeño tutorial.

2º) Descargar los plugins de AspectJ. Desde la misma página de NetBeans, vamos a la sección de plugins y buscamos en el catálogo todos los plugins que coincidan con "aspectj". Descargamos el más nuevo.

3º) Agregar los plugins a NetBeans. Descomprimimos el archivo descargado del paso anterior en una carpeta temporal. Luego vamos a NetBeans, y al menú "Herramientas | Complementos". Allí seleccionamos la pestaña "Descargados" y agregamos todos los archivos que salieron de la extracción del archivo comprimido. Luego los instalamos y listo.


En este punto ya tenemos instalado el plugin de AspectJ, es decir, ya podemos agregar aspectos a nuestros proyectos. Ahora queda un últimp paso, que es configurar el proyecto que al que queremos añadir aspectos. Esto es muy simple:


1º) Añadir la librería "AspectJ-Runtime". Para esto vamos a las propiedades del proyecto, y en Librerías seleccionamos la pestaña "Compilación". Le damos click a "Añadir librería" y seleccionamos la que se llama AspectJ-Runtime.


Listo, con esto ya podemos empezar a hacer uso de los aspectos en Java bajo NetBeans. Simple, ¿no? A mi me costó al no encontrar una buena página que describiera los pasos. Al final recién di con la que mencioné al principio.

S2!