La refactorizaci�n, un proceso y un conjunto de t�cnicas para reorganizar el c�digo preservando su comportamiento externo, ha ganado popularidad debido a su valor pr�ctico en la creaci�n de c�digo �gil. Recientemente la Programaci�n Orientada al Aspecto (AOP) ha recibido una atenci�n creciente debido a su poder de encapsular crosscutting concerts (a lo largo de estas p�ginas utilizaremos el t�rmino crosscutting concerts para referirnos a las "propiedades de un sistema que tienden a estar presentes en varios componentes funcionales") en un sistema a trav�s del uso de una nueva unidad de modularidad llamada aspecto. La refactorizaci�n orientada al aspecto igualmente combina estas dos t�cnicas para refactorizar elementos redundantes. En esta dos p�ginas, exminaremos los fundamentos de la refactorizaci�n orientada al aspecto, los pasos implicados en el proceso, y unas cuantas t�cnicas de las m�s comunes,
La Refactorizaci�n Orientada al Aspecto (Refactorizaci�n AO) va m�s all� de las t�cnicas de refactorizaci�n convencionales. Aunque algunos pasos de la refactorizaci�n convencional modularizan el c�digo en una implementaci�n l�mpia de OO (Orientada a Objetos), el uso de AOP exprime el c�dido que no puede ser refactorizado. La Refactorizaci�n Orientada al Aspecto ofrece mejoras substanciales en gran variedad de situaciones como en las pol�ticas de manejo de excepciones, el refuerzo de contratos locales, el control de concurrencia y la creaci�n de objetos worker.
La Refactorizaci�n AO ofrece muchos beneficios. Inicialmente, su parte m�s atractiva es la implementaci�n de la misma funcionalidad con menor n�mero de l�neas. Sin embargo, despu�s de utilizarla durante algun tiempo muestra su benefico real en que el c�digo es f�cilmente entendible, altamente consistente y sencillo de modificar.
A trav�s de estas p�ginas, utilizaremos AspectJ para las ilustraciones. Sin embargo, la mayor�a de las t�cnicas se pueden exportar a otros sistemas de AOP como AspectWerkz y JBoss/AOP.
�Los Fundamentos
La Refactorizaci�n AO proporciona un significado adicional a las t�cnicas de refactorizaci�n convencionales. Consideremos el "Extract method" para refactorizaci�n, que encapsula la l�gica en un m�todo separado, mientras deja las llamadas a ese m�todo en m�ltiples sitios potenciales. Con la t�cnica "Extract method calls", de la Refactorizacion AO podemos dar un paso adicional y refactorizar incluso esas llamadas en un aspect separado. La Refactorizaci�n AO tambi�n ofrece unas cuantas t�cnicas por s� misma. Por ejemplo "Extract exception handling" refactoriza los bloques try/catch y cualquier c�digo de manejo de excepciones en un aspect separado, en el proceso de ahorrar una significante cantidad de c�digo as� como de crear un c�digo altamente consistente y manejable.
Consideremos un ejemplo concreto de una t�cnica de Refactorizaci�n AO llamada "Extract method calls". Encapsula la funcionalidad cruzada aislando las llamadas a m�todos en un aspecto separado. Hay muchas ocasiones para dicha refactorizaci�n: log a nivel de clase, chequeo de permisos de seguridad, control de persistencia de la sesi�n (como las llamadas a openSession() y a closeSession() cuando se utiliza Hibernate). La siguiente figura muestra c�mo "Extract method calls" mejora la t�cnica "Extract method":
En la figura anterior, la implementaci�n inicial tiene un trozo de c�digo duplicado en varias partes. La refactorizaci�n "Extract method" encapsula la l�gia duplicada en un nuevo m�todo y reemplaza cada trozo de c�digo original con una llamada al nuevo m�todo. Luego, la Refactorizaci�n AO de "Extract method calls" encapsula esas llamadas a m�todo en un aspecto. El aspecto contiene un pointcut para capturar todos los lugares donde se deber�a llamar al m�todo y avisa a ese pointcut de cuando llamar al m�todo refactorizado. El beneficio m�s importante es la encapsulaci�n de la funcionalidad en el aspecto refactorizado.
La Refactorizaci�n AO es m�s util en crosscutting concerts. En el contexto de AOP, los crosscutting concerts del sistema que se expanden en varias clases y paquetes y reciben la principal atenci�n. Sin embargo, hay crosscutting concerts que afectan a un �mbito peque�o, como aquellos que se aplican s�lo a unos pocos m�todos de una clase. Muchos de los micro crosscutting concerts a nivel de clase, tratan sobre la inicializaci�n de recursos, y el manejo de excepciones espec�ficos de la clase que son candidatos para la Refactorizaci�n AO. Tambi�n puedes aplicar Refactorizaci�n AO en situaciones donde el crosscutting concerts que implica a todo el sistema se aplica restrictivamente a unas pocas clases. De forma frecuente, los aspectos de refactorizaci�n que empiezan con una aproximaci�n restrictiva crecen hasta hacerse ampliamente aplicables.
Aunque el beneficio m�s visible de la Refactorizaci�n AO viene cuando hay m�ltiples localizaciones de un crosscutting concerts, es �til incluso en casos de una �nica localizaci�n para encapsular funcionalidades no-importantes. Esta situaci�n es como cualquier otra en la refactorizaci�n tradicional. Por ejemplo, cuando extraes un m�todo incluso aunque sea el �nico lugar donde se hace una llamada a ese m�todo, obtienes una mejora en el entendimiento del c�digo. Con el tiempo, encontrar�s que hay muchos lugares que requieren una llamada para extraer un m�todo. Similarmente, en Refactorizaci�n AO, te dar�s cuenta que los puntos de uni�n que necesitan la funcionalidad se multiplican. En cualquier caso, la Refactorizaci�n AO localiza problemas de funcionalidad como el refuerzo de contratos y el chequeo de acceso en la implementaci�n principal.
Nota:
La relaci�n entre refactorizaci�n y AOP es mucho m�s profunda. Primero, los desarrolladores pueden introducir aspectos m�s f�cilmente en sistemas donde los elementos del programa (clases, m�todos, etc.) llevan a cabo una responsabilidad simple y bien definida. Teor�camente, los sistemas con dichas caracter�sticas se podr�an crear durante la implementaci�n inicial, aunque en la pr�ctica, son un resultado de los esfuerzos de refactorizaci�n. Segundo, muchas t�cnicas de refactorizaci�n convencionales y adicionales (como extract pointcut y extract base aspect) tambi�n se pueden aplicar a los aspectos. Sin embargo, ninguna de estas situaciones es el foco de este art�culo. |
Como la Refactorizaci�n AO consiste realmente en un conjunto adicional de t�cnicas de refactorizaci�n, dichos principios de refactorizaci�n hacen los cambios en peque�os pasos, utilizan tests de prueba, y los ejecutan antes y despu�s de hacer los cambios, para comprobar que se aplican exactamente como se ha pre-escrito.
�Peculiaridades de la Refactorizaci�n AO
Aunque la mayor�a de los principios conocidos de AOP se contin�an aplicando a la Refactorizaci�n AO, unos pocos de esos principios adquieren una importancia especial o una perspectiva diferente en Refactorizaci�n AO. Consideremos esos principios:
- Aproximaci�n hacia la funcionalidad de los Crosscutting Concerts:
Las discusiones AOP normalmente se enfocan en a�adir una funcionalidad de crosscutting concert a una aplicaci�n existente. En la pr�ctica, el proceso recomendado es primero dise�ar e incluso prototipar una soluci�n convencional. Luego puedes dise�ar e implementar aspectos para encapsular esa funcionalidad. La aproximaci�n a la refactorizaci�n es similar. Cuando ya tienes una soluci�n convencional que funciona, la Refactorizaci�n AO te ayuda a encapsular la funcionalidad implementada. - Aplicabilidad del aspecto al crosscutting concert:
Cuando se implementa la funcionalidad utilizando aspectos, una aproximaci�n pragm�tica clama por restringir el crosscutting concert a unas partes seleccionadas del sistema. De esta forma limitamos el efecto de esos aspectos. Luego podr�amos incrementar poco a poco el �mbito de los aspectos, algunas veces apuntando a toda la aplicaci�n no restringida. El uso de la refactorizaci�n pone un �nfasis extra en esta aproximaci�n. La refactorizaci�n de aspectos limita deliberadamente los crosscutting concerts a s�lo unos m�todos o una clase, y algunas veces, a un paquete. Los puntos de corte basados en l�xico como within() y withincode() son muy manejables para imponer las restricciones deseadas. Aunque las t�cnicas de Refactorizaci�n AO empiezan con una aproximaci�n muy restrictiva, tienen mucho potencial para toda una aplicaci�n. - Consideraciones de acoplamiento:
En toda la utilizaci�n de AOP, es deseable minimizar el acoplamiento entre los aspectos y las clases. Sin embargo, en la utilizaci�n de la refactorizaci�n, los problemas de acoplamiento reciben un �nfasis menor. La refactorizaci�n de un aspecto forma parte de la implementaci�n de la clase y por lo tanto podr�a utilizar un conocimiento �ntimo de la clase (como nombres de variables espec�ficos o un lista de m�todos expl�cita). Por supuesto, si hay alguna forma de minimizar el acoplamiento, siempre es deseable su utilizaci�n. - Situaci�n de los Aspectos:
En la utilizaci�n de la refactorizaci�n, como los aspectos podr�an tener que cambiar con la implementaci�n, es deseable ponerlos cerca del m�dulo objetivo. Por ejemplo, si el objetivo de la refactorizaci�n es una clase, podr�amos implementar los aspectos de refactorizaci�n en el mismo fichero fuente, e incluso como aspectos anidados. Aunque en el uso de la AOP se emplean aspectos anidados; en la refactorizaci�n consiguen un �nfasis adicional.
En suma, la Refactorizaci�n AO se construye sobre los principios y pr�cticas m�s comunes de AOP, con cambios en el �nfasis de algunos pocos. Como podr�as esperar, con cualquier tipo de utilizaci�n, la AOP contin�a ofreciendo beneficios como una mejor modularizaci�n, aumentar la comprehensi�n del c�digo y mejorar su mantenimiento.
�Un Ejemplo: Refactorizar la Extracci�n de Llamadas a un M�todo.
Examinaremos el proceso de la Refactorizaci�n AO a trav�s de un ejemplo de unas de las t�cnicas m�s simples de "Extract method calls". Utilizaremos una clase sencilla con unos pocos m�todos ya que con la refactorizaci�n como objetivo nos permitir� enfocarnos en el proceso. Este ejemplo muestra como utilizar de forma efectiva las herramientas disponibles para la Refactorizaci�n AO hasta que los IDEs proporcionen soporte autom�tizado, igual que lo han hecho para la refactorizaci�n convencional.
Ten en mente, que debido a la extrema simpleza del ejemplo y a la t�cnica de refactorizaci�n utilizada, algunos pasos podr�an parecer in�tiles, pero son muy �tiles en sistemas reales. Por la misma raz�n, no ver�s mucho ahorro de l�neas de c�digo. En la p�gina siguiente veremos t�cnicas como "Extract exception handling" que ofrecen un ahorro substancial en l�neas de c�digo. El objetivo de est� p�gina es presentar el sabor y el proceso de la Refactorizaci�n AO.
Consideremos la implementaci�n de la clase Account del siguiente listado que realiza un chequeo de permisos al principio de todos los m�todos. Tomaremos este c�digo como punto de partida y a trav�s algunas iteraciones llegaremos a un c�digo bien-refactorizado.
Como puedes ver, no hay mucho que dejar para que una t�cnica convencional pueda refactorizar los chequeos de permisos: en casi todos los m�todos hay una llamada al m�todo checkPermission().
Para la refactorizaci�n "Extract method call", divideremos el proceso de factorizaci�n en dos pasos obligatorios seguidos por otros dos pasos opcionales. Para otros tipos de refactorizaci�n, los pasos variar�n un poco, pero el estilo ser� el mismo. Al final de cada paso, el comportamiento deber�a ser igual que el del c�digo original, y por lo tanto, deber�amos ejecutar unidades de test para asegurarnos de que la refactorizaci�n no ha cambiado su comportamiento - despu�s de todo, esto sobresable en cualquier esfuerzo de refactorizaci�n.
�Paso 1: Introducir un Aspecto de Refactorizaci�n No-Operacional
El prop�sito de este paso es crear la infraestructura requerida (crosscutting concert est�tico y din�mico), pero sin a�adir funcionalidad al problema. En esencia, est�mos limitados a declarar errores y a declarar avisos y consejos sin ejecutar ning�n c�digo adicional.
El primer paso se puede dividir en tres sub-pasos:
- Insertar un aspecto vac�o:
Insertamos un aspecto vac�o anidado que eventualmente implementar� un chequeo de permisos para la clase Account. Observa que el uso de un aspecto anidado (o un aspecto emparejado en el mismo fichero fuente) simplifica el seguimiento de los cambios en el c�digo refactorizado. El aspecto se parece a esto:
- Definir un pointcut para capturar los puntos de uni�n que necesitan la funcionalidad refactorizada:
Aqu� hemos creado un pointcut que captura todos los puntos de uni�n donde nos gustar�a a�adir la funcionalidad refactorizada. Para minimizar los efectos no deseados, el pointcut simplemente enumera todos los m�todos necesarios. Aqu� podemos ver la definici�n del pointcut:
Aunque hemos utilizado un pointcut indivual totalmente especificado, podriamos elegir la omisi�n de algunas partes (como las excepciones declaradas en cada m�todo) o reemplazar alguna parte con comodines (utilizaci�n de ".." en lugar de especificar todos los argumentos). El uso de within() se asegura de que el pointcut no captura ning�n punto de uni�n fuera de la clase Account. En nuestro ejemplo, el uso de within() es redundante y s�lo sirve para enfatizar el �mbito del crosscutting concert. - Crear un advice no-operacional para el pointcut:
Dependiendo de la posici�n necesaria de la l�gica del crosscutting concert, utilizamos una forma de advice (consejo) apropiado (before, after, after returning, after throwing, o around). Observa que si elc�digo del crosscutting concert requiere ejecutar c�digo en varias posiciones con respecto a los puntos de uni�n, necesitaremos m�s de un consejo, por ejemplo un consejo before y un consejo after. Si utilizas before o after, deja el cuerpo vac�o y en el caso de utilizar un consejo around, s�lo a�ade una sentencia proceed() dentro del cuerpo.
Como en nuestro caso necesitamos ejecutar el chequeo de acceso antes de la ejecuci�n de la l�gica de negocio, un consejo before es la elecci�n apropiada.
Aqu� est� el c�digo fuente despu�s de implementar este paso:
Ahora examinemos si hemos capturado correctamente todos los puntos de uni�n requeridos. Podemos utilizar el soporte integrado de AspectJ en los IDE�s para hacer m�s sencilla la inspecci�n visual. La siguiente figura muestra una vista del c�digo en Eclipse:
Como muestra la figura anterior, puedes ver todos los sitios donde se aplica el consejo. Podr�as pulsar en todos los m�todos aconsejados para ver si de hecho ese sitio tiene una llamada al m�todo refactorizado. Hasta ahora, no hemos modificado nada de la funcionalidad.
�Paso 2: Introducir la Funcionalidad del Crosscutting
En este paso, finalmente moveremos c�digo de la clase principal al aspecto. Tambi�n realizaremos alg�n trabajo adicional para ayudar en la implementaci�n correcta y nos acordaremos de refactorizar el aspecto en el futuro.
- Introducir avisos en tiempo de compilacion (Opcional):
Este paso recomendado utiliza la facilidad de AspectJ de avisos en tiempo de compilaci�n especificables por el usuario para expresar nuestra intenci�n de refactorizar el aspecto - y no la clase - y hacer las llamadas necesarias. Adem�s, dicha construcci�n lanzar� avisos en caso de que un programador, despreocupado por el aspecto, introduzca de nuevo el m�todo refactorizado en la clase.
Observa que querr�s dejar la sentencia declare warning para avisar a alg�n nuevo desarrollador que no deber�a haber ning�n m�todo checkPermission() en ning�n lugar de la clase. - Anadir Funcionalidad crosscutting al Consejo:
Ahora a�adiremos las llamadas de m�todos necesarias en el advice. En nuestro caso, a�adiremos llamadas a AccessController.checkPermission():
- Eliminar las llamadas a m�todos de los m�todos aconsejados:
Como el advice realiza el chequeo de permisos, necesitamos eliminar las llamada de cada uno de los m�todos aconsejados. Por lo tanto, es hora de eliminar esos chequeos de todos los m�todos. Puedes utilizar la vista del outline del IDE para encontrar todos los sitios capturados por el pointcut y simplemente eliminar las llamadas. Si te olvidas de alg�n m�todo, la sentencia declare warning lanzar� un aviso como el de la siguiente figura:
En un sistema m�s grande, podr�amos queres escribir un consejo around para hacer que estas llamadas a m�todos no fueran operacionales y testear los resultados antes de borrar realmente el c�digo. Por ejemplo, en nuestro sistema, podr�amos introducir el siguiente consejo para llamadas nulas al m�todo AccessController.checkPermission() de la clase Account. Observa que el pointcut utilizado aqu� es el mismo que el de la sentencia declare warning anterior. Una vez pasado el test, podr�as eliminar el consejo:
El siguiente listado muestra el c�digo de Account.java despu�s de terminar el segundo paso:
Ahora ya hemos eliminado del c�digo principal todos los chequeos de permisos y los hemos encapsulado en un aspecto separado. Podr�amos parar aqu�. Sin embargo, deber�as considerar otros dos pasos opcionales. Aunque el primer paso opcional ayudar� si evoluciona de cierta forma el c�digo de la clase refactorizada, el segundo paso ayudar� a extraer partes reutilizales del aspecto refactorizado.
�Paso 3 (Opcional): Simplificar la Definici�n del Punto de Corte.
En este paso opcional, redefinimos el pointcut para hacerlo m�s peque�o y sem�nticamente m�s significativo. Observa que en el paso 2, el pointcut definido capturaba s�lo los m�todos enumerados con una firma exacta a la suministrada. Esto nos ayuda a garantizar la preservaci�n del comportamiento general, por ahora. Sin embargo, si se a�aden posteriormente m�todos que necesitan la misma funcionalidad, hacer esto requerir� la modificaci�n de la definici�n del pointcut. Adem�s, tambi�n nos gustar�a protegernos contra los cambios en las firmas de los m�todos que podr�an evitar que se aplicara el consejo a los m�todos modificados. En este paso, especificaremos una definici�n alternativa del pointcut para evitar este problema.
Observa que este paso no es un paso mec�nico y requiere entendimiento de la interacci�n con la funcionalidad y la diligencia en la implementaci�n. Como se podr�a llegar a un pointcut equivalente por muchos caminos, es importante que partamos de una expresi�n pointcut que sem�nticamente capture todos los puntos de uni�n requeridos. En la mayor�a de las ocasiones, es una buena idea capturar un punto de corte que capture un amplio conjunto de puntos de uni�n basandose en la sem�ntica y luego tener en cuenta los puntos de uni�n excepcionales. Por ejemplo, podr�amos capturar todos los m�todos p�blicos si les aplica la funcionalidad a todos los m�todos disponibles externamente o a todos los m�todos cuyo nombre empiece con "set" con cualquier modificador de estado. Si hay excepciones, podr�amos mejorar el punto de corte con los casos excepcionales. De esta forma cualquier modificaci�n en nuestro c�digo (m�todos a�adidos o renombrados) ser� capturada correctamente.
En la clase Account, observamos que todos los m�todos p�blicos excepto toString() llaman al m�todo AccessController.checkPermission(). Capturemos est� observaci�n modificando la definici�n del pointcut. En este punto, podr�as querer utilizar la vista crosscutting del IDE par ver si hemos capturados los m�todos correctos. Observa que la inspecci�n manual es tediosa y propensa a errores cuando se capturan puntos de corte en un gran n�mero de puntos de uni�n. (Al final de esta p�gina podr�s ver una nota con una t�cnica alternativa para realizar esto).
El siguiente listado muestra la clase Account.java despu�s de terminar el tercer paso:
Dado el tipo de cambios que hemos realizado, este es un momento especialmente bueno para ejecutar nuestros tests de prueba.
�Paso 4 (Opcional): Refactorizar el Aspecto de Refactorizaci�n
En este paso, podr�amos refactorizar el propio aspecto de refactorizaci�n. Siguiendo el proceso �gil wisdom, probablemente querr�s esperar hasta que veas que alguna otra clase necesita la misma refactorizaci�n. El proceso implica normalmente la creaci�n de un aspecto abstracto y mover a �l la mayor�a de las funcionalidades. El aspecto base contiene unos cuantos puntos de corte y m�todos abstractos. Los aspectos de refactorizaci�n concreta de cada m�dulo espec�fico extienden este aspecto y proporcionan definici�n para los puntos de corte e implementaci�n para los m�todos. En un sentido, dicha refactorizaci�n es el equivalente AOP de la refactorizaci�n "Extract superclass", llamado "Extract base aspect".
�Utilizar Crosscutting Est�ticos para Marcar Puntos de Uni�n no Capturados
Aunque en clases muy simples (como la clase Account), podr�as crear un punto de corte directamente con una definici�n corta, para casos m�s complejos, hay una t�cnica mejor que ayuda a evitar los errores entre la definici�n del punto de corte original y su nueva versi�n. En esta t�cnica, creamos un punto de corte temporal que captura los puntos de uni�n seg�n su sem�ntica. En nuestro ejemplo de la clase Account, "parece" que todos los m�todos p�blicos necesitan chequeo de permiso. Por lo tanto, definiremos el punto de corte para capturar todos los m�todos p�blicos de la clase Account. Este punto de corte reemplazar� la definici�n del punto de corte permissionCheckedExecution(). Ahora utilizaremos un idioma que produce errores si dos puntos de corte no coinciden exactamente en el mismo conjunto de puntos de uni�n. Observa que este idioma es s�lo posible si ambos puntos de corte son est�ticamente determinables, lo que significan que no deben utilizar puntos de corte this(), target(), args(), cflow(), cflowbelow(), ni if(). Frecuentemente, podemos refactorizar un mismo punto de corte para separar las partes est�ticamente derterminables del resto, permitiendo el uso del idioma presentado. La sentencia que implementa este idioma para nuestro ejemplo, es esta: Cualquier error lanzado por el compilador implica que dos puntos de corte no son equivalente. De la forma que los hemos configurado, obtenemos un error porque temp() captura el m�todo toString() pero no a permissionCheckedExecution(). Como aparece en la figura anterior, el error apunta al m�todo toString(). Reparemos esto elimando este m�todo de los puntos de uni�n capturados. Ahora el compilador no producir� ning�n error: Observa que alguno de estos errores podr�an revelar bugs en el c�digo original, como llamadas a m�todos saltadas desde otros m�todos. Unos tests de pruebas inadecuados podr�an haber dejado esos bugs en forma latente. En dichos casos, podr�a utilizar esta oportunidad para corregirlos y actualizar los tests. Ahora que tenemos un punto de corte equivalente, cambiamos su nombre por el del punto de corte original y eliminamos �ste. Esta es la misma definici�n de punto de corte que creamos utilizando mera observaci�n, como se describi� en el paso 3. |