• No se han encontrado resultados

Refactorización automatizada para la eliminación de Feature Envy

N/A
N/A
Protected

Academic year: 2020

Share "Refactorización automatizada para la eliminación de Feature Envy"

Copied!
74
0
0

Texto completo

(1)

TESIS DE GRADO EN INGENIERÍA DE SISTEMAS

REFACTORIZACIÓN AUTOMATIZADA PARA LA

ELIMINACIÓN DE FEATURE ENVY

por

Antivero, Juan Pablo

Arias, Lucas

Director: Vidal, Santiago A.

Co-Directora: Marcos, Claudia A.

FACULTAD DE CIENCIAS EXACTAS

UNIVERSIDAD NACIONAL DEL CENTRO DE LA PROVINCIA DE

BUENOS AIRES

(2)

2

Índice

Capítulo 1: Introducción ... 8

1.1 Calidad de software ... 8

1.2 La problemática de los Code Smell ... 9

1.3 Extensión de Bandago ... 10

1.4 Esquema general ... 12

Capítulo 2: Calidad y mantenimiento de software ... 13

2.1 Calidad de software ... 13

2.2 Refactoring ... 14

2.3 Code Smells ... 15

2.3.1 Feature Envy ... 17

2.3.2 Refactoring de Feature Envy ... 18

2.3.2.1 Program Slicing ... 18

2.3.2.2 Statements ... 18

2.3.2.3 Problemática del Move Method ... 19

Capítulo 3: Herramientas de refactorización y análisis de

Feature Envy ... 20

3.1 Enfoques del refactoring de Feature Envy ... 20

3.2 Herramientas ... 20

3.2.1 Herramientas de recomendación de refactoring ... 20

3.2.1.1 MethodBook ... 21

3.2.1.2 MORE ... 22

3.2.1.3 c-JRefRec ... 25

3.2.1.4 JMove ... 27

3.2.2 Herramientas de aplicación de refactorings ... 28

3.2.2.1 Eclipse ... 29

3.2.3 Herramientas de recomendación y aplicación de refactorings ... 29

3.2.3.1 JDeodorant ... 29

3.3 Criterios de comparación ... 30

(3)

3

Capítulo 4: Un enfoque iterativo para la refactorización de

Feature Envy ... 33

4.1 Refactorización de Feature Envy ... 33

4.2 Esquema general de la solución iterativa de Feature Envy ... 34

4.3 Desarrollo del enfoque iterativo para la refactorización de Feature Envy ... 36

4.3.1 Actividad 1: Obtener características del método ... 36

4.3.2 Actividad 2: Elegir la clase candidata ... 38

4.3.3 Actividad 3: Seleccionar el fragmento de código a mover ... 39

4.3.4 Actividad 4: Calcular las métricas de la solución generada ... 41

4.3.5 Actividad 5: Aplicar el refactoring ... 42

4.3.6 Actividad 6: Corregir errores... 43

4.4 Implementación de la solución ... 44

4.4.1 Aplicación de refactorings ... 44

4.4.2 Vista arquitectónica de Bandago ... 45

Capítulo 5: Casos de estudio ... 49

5.1 Proyectos evaluados y operatoria de la evaluación ... 49

5.2 Características de las Feature Envy ... 51

5.3 RQ #1: ¿Qué porcentaje de Feature Envy son automáticamente eliminadas por la tool, sin necesidad de correcciones por parte del desarrollador? ... 54

5.3.1 Hipótesis ... 54

5.3.2 Análisis e Interpretación ... 54

5.4 RQ #1: ¿Qué porcentaje de Feature Envy son automáticamente eliminadas por la tool, sin necesidad de correcciones por parte del desarrollador? ... 59

5.4.1 Hipótesis ... 59

5.4.2Análisis e Interpretación ... 60

5.5 RQ #1: ¿Qué porcentaje de Feature Envy son automáticamente eliminadas por la tool, sin necesidad de correcciones por parte del desarrollador? ... 64

5.5.1 Hipótesis ... 64

5.5.2 Análisis e Interpretación ... 65

Capítulo 6: Conclusión ... 69

6.1 Conclusiones finales ... 69

(4)

4

6.3 Limitaciones ... 70

6.4 Trabajos futuros ... 71

(5)

5

Índice de Figuras

1.1 Esquema base de la herramienta ... 11

2.1 Etapas del proceso de refactoring ... 14

2.2 Detección de Feature Envy ... 17

3.1 Proceso utilizado por MethodBook para encontrar recomendaciones ... 21

3.2 Arquitectura del recomendador de refactorings MORE ... 23

3.3 Vista Class State View de la herramienta c-JRefRec ... 26

3.4 Vista Refactoring Candidates View de la herramienta c-JRecRef ... 26

3.5 Interfaz de JMove ... 28

3.6 Presentación de Feature Envy de JDeodorant ... 30

4.1 Correlación entre code smells. Lanza y Marinescu ... 33

4.2 Esquema general de la solución de Feature Envy ... 35

4.3 JSpIRIT Smells View ... 37

4.4 Llamados a variables de instancia de la propia clase del método ejemplo... 37

4.5 Identificación de llamados a clases externas ... 39

4.6 Selección de statement o conjunto de statements a extraer ... 40

4.7 Selección del statement candidato a ser extraído para la clase name.gyger.jmoney.model.Entry... 41

4.8 Cálculo de las métricas de la solución generada (Bandago View) ... 42

4.9 Selección de una solución para Feature Envy (Bandago Wizard)... 43

4.10 Método movido a la clase candidata... 43

4.11 Extracción de un método previo a ser movido ... 45

4.12 Diagrama de contexto de Bandago ... 46

4.13 Arquitectura de alto nivel de Bandago ... 48

5.1 Diagrama de la operatoria del experimento ... 51

5.2 Boxplot para la métrica ATFD ... 52

(6)

6

5.4 Boxplot para la métrica FDP ... 53

5.5 Extracción Total vs Extracción Parcial ... 58

5.6 Cantidad de soluciones identificadas ... 58

5.7 Limitaciones de Eclipse al aplicar el refactoring Move Method ... 59

5.8 Cantidad de smells solucionados por el enfoque ... 61

5.9 Porcentajes de smells solucionados ... 62

5.10 Cantidad de smells creados por el enfoque ... 62

5.11 Porcentajes de smells creados... 63

5.12 Code Smells afectados por el enfoque ... 64

5.13 Cantidad de Feature Envy identificadas por JDeodorant y JSpIRIT ... 65

5.14 Porcentaje de resolución de ambas herramientas sobre la intersección ... 66

5.15 Método solucionado por JDeodorant y no solucionado por el enfoque propuesto ... 66

5.16 Cantidad de Feature Envy resueltas por ambas herramientas ... 67

(7)

7

Índice de Tablas

2.1 Code Smells catalogados por Lanza y Marinescu ... 16

2.2 Métricas utilizadas por la herramienta... 17

3.1 Selección de refactorings basado en reglas de MORE ... 25

3.2 Categorías de las herramientas ... 31

3.3 Características de las herramientas ... 31

4.1 Priorización de clases externas accedidas por la Feature Envy ... 39

4.2 Elementos de la arquitectura de Bandago... 46

5.1 Proyectos evaluados ... 49

5.2 Cantidad de Feature Envy por proyecto ... 51

5.3 Resultados de Feature Envy analizadas y resueltas automáticamente ... 55

5.4 Resultados de Feature Envy analizadas y resueltas luego de la intervención por parte del desarrollador ... 56

5.5 Resultados de Feature Envy analizadas y resueltas ... 57

(8)

8

Capítulo 1: Introducción

1.1

Calidad de software

En los sistemas de software es muy importante centrar el desarrollo en su calidad [22,23]. En el contexto del software, calidad es "la totalidad de aspectos y características de un producto o servicio que tienen que ver con su habilidad de satisfacer las necesidades explícitas o implícitas" [12]. Para asegurar esto es que existe el ciclo de vida del software, metodologías de desarrollo, modelos de mejora continua de procesos y distintos aspectos relacionados al área de SQA (Sofware Quality Assurance, es decir, Garantía de la Calidad de Software) [24].

Sin embargo, la calidad y la estructura de los sistemas se ven deterioradas a menudo, generalmente como resultado de los cambios que introducen los programadores para resolver objetivos a corto plazo, sin conocer completamente el diseño del código [2]. Estos problemas de diseño normalmente son conocidos como code smells. Un code smell (también llamado bad smell), se refiere a cualquier síntoma que puede indicar que el código fuente presenta problemas [2], como lo es el código duplicado, métodos o clases muy largos, mal encapsulamiento de clases o extensas listas de parámetros, entre otros.

Feature Envy es un code smell que impacta principalmente en la modificabilidad del sistema. Lanza y Marinescu [3] lo definen de la siguiente manera:

“La desharmonía de diseño Feature Envy se refiere a los métodos que parecen más interesados en los datos de otras clases que en los de su propia clase. Estos métodos acceden o una gran cantidad de datos de otras clases directamente o mediante métodos de acceso”

La presencia de Feature Envy afecta negativamente el mantenimiento y la extensibilidad, minimizando la cohesión de una clase y generando el riesgo de sufrir un efecto en cascada (un cambio en un método dispara cambios en otro método y así sucesivamente).

Es importante señalar que la calidad del software no se refiere únicamente a obtener un producto sin errores, sino también a que su código fuente se pueda extender, modificar de manera simple y entender sin que esto resulte un problema con el paso del tiempo, debido a que la etapa posterior a la entrega es la parte más importante en el desarrollo de software [14]. La existencia y aumento de code smells generan una degradación de la calidad del software, impactando fuertemente sobre la comprensión, mantenimiento y evolución del código fuente [1].

(9)

9

modularización y evitando así, en muchos casos, una mayor cantidad de código para realizar una misma tarea debido a código duplicado.

1.2 La problemática de los Code Smell

A pesar de la importancia del proceso de refactoring como medio para mantener la calidad del software, éste no siempre es realizado con la frecuencia ni con el tiempo que requiere. La falta de refactorings, en general, se debe a lo complejo y costoso que puede resultar el proceso de detección de code smells, la forma de solucionarlos, y el riesgo de generar errores durante el proceso. También, en la industria, la frecuencia y tiempo que se le dedica a este proceso se ve afectada por los cortos tiempos de entrega que se disponen.

El autor K. Beck, en uno de sus libros sobre la metodología ágil eXtreme Programming, afirma que el refactoring ahorra tiempo en desarrollo y mejora la calidad [18], instando a los desarrolladores a incluir al refactoring como una etapa que sea parte del ciclo de desarrollo del software. Por otro lado, existe la idea de que los ingenieros de software a menudo evitan el refactoring cuando se encuentran limitados con respecto a recursos y tiempos de entrega [16]. En general, muy pocos desarrolladores se acercan a un equilibrio entre la adición de nueva funcionalidad y la utilización de refactoring. En cualquiera de los casos, la falta de herramientas que determinan el impacto real del refactoring y sus efectos secundarios, contribuyen con la decisión de posponer el refactoring reiteradas veces, y en la mayoría de los casos nunca es ejecutado [17].

Un estudio de campo realizado por Kim, Zimmermann y Naggapan [19] muestra algunas contradicciones en sus resultados acerca de los beneficios de aplicar refactorizaciones. Aunque muchos programadores afirman que el uso de refactoring disminuye la cantidad de bugs, otros señalan que en muchas ocasiones aparecen bugs de regresión al refactorizar. Sin embargo, la causa de este inconveniente ha sido analizada por Murphy-Hill et al. [21], que encontró que desarrolladores usualmente ejecutan refactorings junto con otros cambios que alteran el comportamiento, además que los refactorings son aplicados en forma manual en lugar de utilizar una herramienta que automatiza el proceso. Este tipo de prácticas son propensas a introducir errores en el código.

La Feature Envy es un code smell sobre el cual se posee poca información que permita inferir un método automatizado intuitivo para eliminarla. Martin Fowler [2] plantea que la solución consiste en mover el método a la clase de la cual envidia el mismo mediante el refactoring Move Method. En caso de que únicamente una parte del método sufra esta anomalía, previamente hay que extraerla mediante Extract Method para luego realizar el Move únicamente de esa parte.

Realizar manualmente este proceso puede ocasionar problemas de compilación y/o de modificabilidad. Se deben comprobar las dependencias para no correr el riesgo de afectar el funcionamiento del código, ya sea generando errores o incluso cambiando el comportamiento original del programa accidentalmente.

(10)

10

 El código seleccionado para extraerse tiene que ser una lista de sentencias.

 Dentro del fragmento de código no pueden existir asignaciones a variables que sean utilizadas luego en el flujo del método. En el caso de Java, se puede permitir la asignación de una variable y ésta será la variable de retorno en el método.

 No pueden existir sentencias del tipo return.

 El bloque de código no puede contener ramas de ejecución que vayan por fuera del código seleccionado.

En cuanto al uso de Move Method, adicionalmente se deben tener en cuenta las condiciones que imposibilitan la realización del mismo:

 El método no debe estar declarado en superclases o subclases.

 Deben modificarse todos los llamados que referencien a ese método.

 En la clase candidata a la cual se moverá no debe existir un método del mismo nombre.

 El método no debe sobreescribir un método abstracto.

 El método debe referenciar a la clase candidata a través de sus parámetros o de los campos de la clase original.

Debido a la complejidad que conlleva el control de todos los puntos para asegurar que no se ha modificado la funcionalidad del sistema ni se han introducido errores de compilación, es importante encontrar la forma de automatizar la refactorización de Feature Envy.

Pocos estudios intentan solucionar code smells con una refactorización automatizada [25] [40] [41]. En el caso de Feature Envy, existen herramientas que intentan lograrlo. Sin embargo, ninguna solución conocida intenta la extracción (Extract Method) previa al Move Method.

Existen herramientas, como JDeodorant [25] que han logrado una correcta solución a este code smell, siempre y cuando se pueda mover el método completo a la clase candidata. Se requiere realizar un análisis más detallado para lograr la identificación de métodos en los que únicamente es necesario mover una parte del mismo.

1.3 Extensión de Bandago

En este trabajo se presenta una extensión de la herramienta Bandago [20] para el code smell Feature Envy. Bandago es una herramienta que tiene como objetivo la solución de code smells y luego el análisis del costo beneficio de la solución mediante la presentación de métricas pertinentes. Originalmente dicha herramienta poseía la funcionalidad para solucionar el code smell Brain Method, y fue extendida para soportar la resolución de Feature Envy.

(11)

11

para extraer la parte del método que genere Feature Envy y moverla a la clase candidata. Así se logra poner en un mismo lugar el método y los datos que se utilizan conjuntamente [2], minimizando la posibilidad de "efectos cascada" (un cambio en un método dispara cambios en otro método sucesivamente) y se maximiza la cohesión [3].

Figura 1.1: Esquema base de la herramienta.

La Figura 1.1 muestra un esquema del enfoque propuesto donde se observa la Feature Envy como la entrada, el código refactorizado como la salida y componentes que fueron utilizados, encargados de realizar el análisis del código afectado y de aplicar el refactoring. Se realizó un enfoque heurístico sobre la herramienta Bandago [20] y se utilizó como entrada la herramienta JSpIRIT [13] para la detección de los code smells. El análisis de las métricas tiene como base las investigaciones de Lanza y Marinescu [3]. También, se estudiaron las herramientas que provee el IDE Eclipse para la extracción de código, y especialmente para la realización de Move Method, debido a que actualmente la API de Eclipse no provee un soporte completo para realizar correctamente este refactoring programáticamente.

Para poder validar los beneficios del enfoque se realizó un caso de estudio donde se respondieron 3 research questions. Estas fueron:

 RQ#1: ¿Qué porcentaje de Feature Envy son automáticamente solucionadas por el enfoque propuesto, sin necesidad de intervención del desarrollador?

 RQ#2: ¿Solucionar las Feature Envy permite solucionar otros Code Smells?

 RQ#3: ¿Son complementarias las soluciones identificadas por el enfoque propuesto y por JDeodorant en la refactorización de Feature Envy?

(12)

12

La refactorización de este code smell lleva a una mejora en la cohesión y el acoplamiento del software. Se obtuvo como resultado un código fuente mejor estructurado, más legible y con mejor diseño.

Las contribuciones del trabajo final son las siguientes: Se logró automatizar todo el proceso de refactorización de las Feature Envy, generando una solución válida. Se pueden visualizar el conjunto de métricas relacionadas que justifican la aplicación de la solución propuesta. Además, se extendió la funcionalidad de Bandago para hacerlo más configurable a las necesidades de un desarrollador. Otro aporte importante es el análisis automatizado de posibilidades de extracción en caso de no poder mover el método completo, ya que no existe precedencia en la aplicación de este análisis adicional al intentar resolver una Feature Envy.

1.4 Esquema general

El esquema general de la tesis está organizado de la siguiente manera:

En el capítulo 2 se presentan los conceptos de la calidad y mantenimiento de software. Así mismo, se introducirán temas clave como la noción de code smell y refactorización.

En el capítulo 3 se describen herramientas enfocadas a refactorizar automáticamente una Feature Envy. Adicionalmente, serán comparados entre sí.

En el capítulo 4 se presenta el enfoque de la solución planteada. Se desarrollarán las actividades y se describirán detalladamente. Luego, se presentarán la implementación y el diseño de la solución.

En el capítulo 5 se presenta un caso de estudio que responde las research questions. Se presenta la operatoria que se utilizó en el caso de estudio y posteriormente el análisis de los resultados obtenidos.

(13)

13

Capítulo 2: Calidad y mantenimiento de software

2.1 Calidad de software

En el contexto del software, calidad es "la totalidad de aspectos y características de un producto o servicio que tienen que ver con su habilidad de satisfacer las necesidades explícitas o implícitas" [12]. Para asegurar esto es que existe el ciclo de vida del software, metodologías de desarrollo, modelos de mejora continua de procesos y todo lo relacionado al área de SQA (Sofware Quality Assurance, es decir, Garantía de la Calidad de Software) [24]. Por lo tanto, es muy importante centrar el desarrollo en su calidad [22,23].

La calidad de software hace referencia a dos puntos importantes: generar código libre de errores y concentrarse en mantener la calidad del código con el paso del tiempo. Este último punto es mucho más complejo para el software que para otros productos industriales [24]. Por lo tanto, la tarea de mantenimiento es dificultosa y requiere de un gran consumo de tiempo si no se desea que se degrade la calidad de software.

Al realizar la tarea de mantenimiento de software, el conocimiento sobre el sistema es importante. Entre el 40% y el 60% del esfuerzo realizado en mantener un sistema está relacionado con el entendimiento del mismo [26]. Esto conlleva a que si es dificultoso obtener la información necesaria sobre requerimientos que el sistema intenta satisfacer, arquitectura utilizada, detalles técnicos, etc. el tiempo invertido en mantenimiento aumentará considerablemente.

Así mismo, esta tarea no es un problema que debe ser resuelto o evitado. Es, en cambio, una solución natural al hecho de que los sistemas de software deben mantenerse en sintonía con el entorno y con las necesidades de los usuarios [26]. Lehman [29] marca como clave el hecho de que los sistemas nunca están completos y siempre continúan evolucionando. A medida que evolucionan, se vuelven más complejos a menos que se realicen acciones para reducir su complejidad.

El mantenimiento de software está dado por 4 necesidades [28]:

1. Modificar el sistema para adaptarlo a nuevas necesidades (aproximadamente el 50% de los proyectos de mantenimiento).

2. Adaptar el sistema a un entorno constantemente cambiante (aproximadamente el 25% de los proyectos de mantenimiento)

3. Corregir errores preventivamente (aproximadamente el 5% de los proyectos de mantenimiento)

4. Corregir errores como resultado de un problema actual (aproximadamente el 20% de los proyectos de mantenimiento)

(14)

14

es el cambio constante de la tecnología y el software, aspecto que se encuentra fuertemente relacionado con el segundo mantenimiento enumerado.

Estos dos mantenimientos, que son los que recubren la mayor cantidad de proyectos, están fuertemente ligados con el refactoring de un sistema. El refactoring lleva a un código más mantenible y extensible. Este lleva a mantener el sistema evolucionable y adaptativo a los cambios de requerimientos del sistema.

2.2 Refactoring

Refactorizar se describe como el proceso de realizar un cambio en la estructura interna del software para que éste sea más fácil de entender y modificar sin cambiar su comportamiento. Es decir, se modifica la estructura interna de un sistema de software, pero sin alterar el comportamiento externo del mismo [29]. Es decir, con el mismo conjunto de entradas, el programa genera la misma salida, antes y después de someterse al proceso de refactoring.

Aunque no cambie el comportamiento del programa, refactorizar puede ayudar al diseño y evolución de un software, reestructurando el programa de una forma que permita introducir cambios más fácilmente [30]. Los cambios extremadamente complicados en programas mal estructurados pueden incluir tanto refactoring como cambios en el comportamiento.

El concepto de refactoring también tiene una estrecha relación con la programación orientada a objetos, debido a que este paradigma hace más factible el uso de esta técnica al hacer más explícita la información estructural necesaria para refactorizar un programa [30]. Algunas de las actividades que se realizan con este objetivo son: reducir la duplicación de código, mejorar la cohesión, reducir el acoplamiento, mejorar atributos de calidad como la legibilidad, flexibilidad, mantenimiento, etc.

Para realizar un refactoring, se deben cumplir las siguientes 3 etapas [31]:

(15)

15

Como se ve en la Figura 2.1, las etapas son:

1. Decisión para refactorizar: Esta etapa ocurre antes de realizar el refactoring propiamente dicho. Consiste en las decisiones previas relacionadas con aspectos personales (quienes lo van a realizar) y la situación actual del código (qué refactorings se aplicarán para generar una solución valiosa).

2. Proceso de refactorización: Hace referencia a realizar las actividades de refactoring. Se deben definir las herramientas que se utilizarán para las mismas.

3. Resultados de la refactorización: Se refiere al análisis posterior, que determinará si se introdujo algún error y si realmente la solución generó algún valor agregado al código existente.

Como se puede apreciar, el proceso de refactorización comienza al analizar la situación actual del código para identificar oportunidades de refactorización. En este paso, para decidir qué refactoring utilizar, se puede utilizar como guía la identificación de code smells [33].

La mayoría de los IDEs actuales soportan este enfoque, proveyendo herramientas para la realizar refactorings de forma semi automática. De esta forma se reducen los tiempos de debugging y testing [34].

2.3 Code Smells

Debido a que se decidió utilizar un code smell como guía para tomar la decisión de refactorización, es importante definir qué es un code smell y por qué es importante identificarlos.

Un code smell surge por la utilización de “malas prácticas” al desarrollar un sistema. Son decisiones pobres de implementación que hacen que un sistema orientado a objetos sea difícil de mantener [35]. Son las descripciones generales de código problemático y sirven para ayudar a los desarrolladores a decidir cuándo un código necesita refactorización [36]. Una vez aplicado el refactoring apropiado, en la mayoría de los casos se mejoran diversos aspectos de calidad como mantenibilidad, comprensibilidad y reusabilidad. [37]

Los code smells surgen como una forma de clasificar de una manera lo más precisa posible los diferentes problemas de diseño que se pueden identificar en un sistema. Dichos problemas fueron detectados inicialmente por los investigadores como no-conformidad con principios de diseño, violación de heurísticas de diseño, valores de métricas excesivos, falta de patrones de diseño o incluso aplicación de anti-patterns [37].

(16)

16

La forma de eliminar un code smell consiste en la aplicación de un refactoring apropiado, mejorando la estructura del software sin modificar su comportamiento [37]. Teniendo conocimiento que los code smells degradan la calidad de un sistema, y sabiendo que el efecto acumulativo de sucesivos refactorings mejora la calidad del diseño, este tratamiento garantiza una mejora sustancial en la calidad del código.

En la siguiente tabla se puede visualizar el catálogo de code smells realizado por Lanza y Marinescu [3]:

Code Smell Descripción

God Class Clases que tienden a centralizar la inteligencia del sistema. Una God Class realiza demasiado trabajo por sí misma, delegando únicamente los detalles menores a un conjunto de clases triviales, usando los datos de otras clases (Normalmente de Data Class). Feature Envy Métodos más interesados en los datos de otras clases

que en los de su propia clase.

Data Class Clases “tontas” qué funcionan únicamente como contenedoras de datos, sin ninguna funcionalidad compleja. Sin embargo otras clases pueden depender fuertemente de ellas.

Brain Method Métodos extensos que tienden a centralizar la funcionalidad de una clase.

Brain Class Similar a God Class, con la diferencia de que a pesar de ser clases excesivamente complejas, no acceden abusivamente a datos de clases “satélite”.

Significant Duplication Porciones de código que contienen una cantidad significativa de duplicación.

Intensive Coupling Método que se encuentra sujeto a demasiadas operaciones en el sistema, y estas operaciones proveedoras están dispersas en pocas clases. Dispersed Coupling Similar a Intensive Coupling, pero las operaciones

proveedoras se encuentran dispersas en muchas clases diferentes.

Shotgun Surgery Método del cual dependen un conjunto amplio de otras operaciones dispersas en muchas clases. Es el caso inverso de Dispersed Coupling.

Refused Parent Bequest Hace referencia a clases hijas en una jerarquía de herencia que no utilizan los métodos declarados por el padre.

Tradition Breaker Hace referencia a clases que en lugar de especializar la clase de la cual heredan, simplemente agregan nuevos métodos que tienen poca relación con aquellos métodos heredados del padre.

Tabla 2.1: Code Smells catalogados por Lanza y Marinescu

(17)

17

2.3.1 Feature Envy

Feature Envy puede ser considerado el síntoma más común relacionado con problemas de acoplamiento y cohesión [38]. Se define como:

“Un método que parece más interesado en los datos de otra clase que en los de su propia clase” [3].

Este code smell surge cuando el desarrollador viola el principio de agrupar el comportamiento con los datos relacionados [38]. Estos métodos acceden directamente a través de métodos accesores (getters) a una gran cantidad de datos de otras clases. Esto provoca un aumento de “efectos cascada” (el cambio en un método dispara cambios en otros métodos y así sucesivamente) y disminuye la cohesión de un sistema. La forma de detectarlo es contar el número de datos externos accedidos, y asegurarse de que los mismos provengan de pocas clases, o de una única incluso. Las métricas que intervienen en este análisis son descriptas en la Tabla 2.2 [3]:

Métrica Descripción

Acceso a datos extranjeros (ATFD) Indica el uso directo de atributos de otras clases. Localidad de los accesos a atributos (LAA) Indica la relación entre el acceso a atributos locales

y el acceso a atributos extranjeros.

Proveedores de datos extranjeros (FDP) Indica la cantidad de clases que proveen atributos extranjeros al método.

Tabla 2.2: Métricas utilizadas por la herramienta

Una vez identificadas las métricas que intervienen, se debe definir un umbral que permita decidir cuándo se está en presencia de una anomalía. Como se ve en la Figura 2.2, las tres métricas intervienen en la identificación, y todas deben superar el umbral especificado para generar una Feature Envy. En caso de que una de las reglas no se cumpla, no se considera la existencia de este code smell.

(18)

18

2.3.2 Refactoring de Feature Envy

Los refactorings utilizados para solucionar una Feature Envy son Move Method y Extract Method. La idea es mover el método que sufre de este code smell a la clase que contiene los datos que envidia. En primer lugar, se deben identificar los casos en los cuales es conveniente realizar primero una extracción del fragmento del método que genera la Feature Envy, para luego mover únicamente ese extracto a la clase correspondiente. De esta forma se encapsulan los datos y la funcionalidad que manejan esos datos en una misma clase, mejorando la modificabilidad y mantenibilidad, al tener en una misma ubicación los fragmentos de código que muy posiblemente se modificarán en conjunto al evolucionar el sistema. De esta forma, se reduce el nivel de acoplamiento entre clases.

El problema principal al aplicar estos refactorings es la posibilidad de afectar el comportamiento original del sistema. Si esto llegase a ocurrir, se estaría violando la definición de refactoring. A la hora de realizar Extract Method, únicamente se pueden extraer fragmentos de código que mantengan la funcionalidad. Para asegurar esto, se utilizará la práctica de Program Slicing [8]. En cuanto al uso de Move Method, se controlarán las dependencias utilizadas por el método antes de realizarlo para no generar errores en el código.

2.3.2.1 Program Slicing

La técnica de Program Slicing es necesaria para realizar Extract Method. Es un método para descomponer programas automáticamente analizando su flujo de datos y de control. El programa reducido, llamado “slice”, es un programa independiente que garantiza una representación fiel del programa original dentro del dominio del subconjunto de comportamiento especificado [39].

Para mantener el control de flujo y datos, se genera un grafo de dependencias que sirve para decidir si es posible extraer la porción (slice) de código. Esta técnica busca extraer la porción más pequeña posible.

Todos estos controles son realizados por la API JDT de Eclipse. Aunque no utiliza la técnica de Program Slicing directamente, es parte de la aplicación del refactoring considerado en este informe.

2.3.2.2 Statements

(19)

19

Debido a que un Statement puede contener más Statements dentro de sí mismo anidados, la forma de obtener todos los Statements de un método es recorrerlos a todos en profundidad hasta que no queden más nodos por recorrer.

2.3.2.3 Problemática del Move Method

(20)

20

Capítulo 3: Herramientas de refactorización y

análisis de Feature Envy

3.1 Enfoques del refactoring de Feature Envy

Para realizar el refactoring de una Feature Envy, se deben realizar dos pasos fundamentales: identificar la oportunidad de refactoring, y luego aplicarla. Las herramientas analizadas en esta sección tienen una particularidad: una vez identificada la Feature Envy, el único refactoring recomendado y/o aplicado es Move Method. Ninguna herramienta considera una extracción previa. En cuanto a las posibles acciones realizadas por las herramientas, se pueden clasificar en tres enfoques:

 Recomendación: Las herramientas incluidas en esta categoría sólo encuentran métodos candidatos y recomiendan la aplicación del refactoring. No distinguen si el refactoring es posible o si realmente se soluciona la Feature Envy al aplicarlo. En este grupo se encuentran las herramientas MethodBook, MORE, c-JRecRef y Jmove (las cuales serán analizadas en este capítulo).

 Aplicación: Las herramientas incluidas en esta categoría realizan el refactoring propiamente dicho, es decir, mueven el método de una clase a otra. No realizan ningún tipo de análisis o recomendación en cuanto al método que se desea mover, únicamente realizan el Move Method siempre y cuando se cumpla la regla de no modificar el comportamiento del sistema ni generar errores de sintaxis. Un ejemplo representativo de este grupo es la API nativa de Eclipse.

 Recomendación y Aplicación: Las herramientas incluidas en esta categoría realizan por completo el proceso de eliminación de code smells. Son más complejas que las anteriores, ya que realizan ambos procedimientos e intentan obtener un resultado válido. Son las únicas que automatizan por completo el proceso de refactorización de Feature Envy. En este grupo se encuentra únicamente la herramienta JDeodorant.

3.2 Herramientas

En esta sección se presentarán las herramientas mencionadas anteriormente, con el fin de profundizar el concepto de los enfoques que cada una realiza.

3.2.1 Herramientas de recomendación de refactoring

(21)

21

3.2.1.1 MethodBook

La herramienta MethodBook [40] propone oportunidades de aplicación del refactoring Move Method con la finalidad de eliminar una Feature Envy.

El enfoque está basado en la técnica probabilística Relational Topic Models (RTM). Consta de dos pasos: Identificar la “amistad entre métodos” e Identificar la clase envidiada.

El primer paso consiste en extraer la información textual y estructural del código fuente. Toda esta información se almacena en diferentes matrices que luego son procesadas por el RTM (Figura 3.1).

Figura 3.1: Proceso utilizado por MethodBook para encontrar recomendaciones

La información textual es representada por palabras en comentarios e identificadores en el código fuente. Se almacena en la “Matriz de Documento Término a Término”. Es utilizada por el RTM para definir relaciones semánticas entre métodos y su modelo de distribución.

La información estructural es diversa, por lo cual MethodBook hace un análisis estático para obtener las dependencias estructurales entre métodos. Almacena los llamados a métodos en la “Matriz de Interacción de Llamados” y el uso de variables compartidas en la “Matriz de Datos Compartidos”. Adicionalmente, en la “Matriz de Diseño Original” conserva la información de a qué clase pertenece cada método, entre otras cosas. Estas matrices estructurales también son utilizadas para generar el modelo de distribución.

(22)

22

sistema. La Matriz de Diseño Original permite decidir si un refactoring afectará positivamente la calidad del diseño del sistema.

Con toda esa información, el RTM genera la “Matriz de Similitud RTM”, que representa la “amistad entre métodos”, es decir, la cantidad de responsabilidades que comparten todos los pares de métodos, las estructuras de datos que comparten y la relación que tienen con las mismas características o conceptos dentro del programa.

Una vez completado el paso de Identificación de Amistad entre métodos, se lo debe llevar a un ámbito en el cual sea posible detectar una Feature Envy. Para comenzar el proceso de Identificación de Clase Envidiada se analizan las clases a las cuales pertenecen los “mejores amigos de cada método”. Si un método en la clase A tiene más amigos en la clase B que en la propia, se presume que comparte responsabilidades con esta última clase, lo cual podría indicar la presencia de Feature Envy. Por esta razón, lo que hace MethodBook es identificar como clase envidiada a la que contenga el mayor porcentaje de métodos amigos. Si la clase envidiada es la propia, no se sugiere ningún refactoring. Si no es el caso, el último paso es analizar estáticamente el código para verificar que se cumplan todas las precondiciones que garanticen mantener el comportamiento original luego de aplicar una operación de refactoring. En caso de cumplirse, sugiere un refactoring. En caso de no cumplirse, elige como nueva candidata a la próxima clase con más alto porcentaje de métodos amigos. Este proceso se repite hasta que se obtiene una clase envidiada que cumpla las precondiciones o la clase envidiada seleccionada sea la propia.

3.2.1.2 MORE

La herramienta MORE [41] es un recomendador automatizado de refactorings. A diferencia de MethodBook, no se encarga únicamente de analizar Feature Envy. Posee tres objetivos principales, los cuales son:

1. Mejorar la calidad de diseño. 2. Resolver code smells.

3. Introducir patrones de diseño.

Para recomendar una operación de refactoring, intenta encontrar el mejor trade-off entre estos tres objetivos utilizando un algoritmo genético llamado “NSGA-III”.

Como se puede inferir, en este trabajo se va a analizar el segundo objetivo de esta herramienta, centrándose en el code smell Feature Envy.

(23)

23

Figura 3.2: Arquitectura del recomendador de refactorings MORE.

El componente llamado Detector de code smells es el encargado de identificar los diferentes code smells existentes en el código. Utiliza un conjunto de reglas de detección, las cuales representan diferentes smells, y las expresa como una combinación lógica de límites de calidad y métricas, es decir, que define los márgenes de aceptación para decidir si el componente que se está analizando es un code smell.

Las reglas que representan un método que sufre Feature Envy, son las que el autor clasifica dentro de los tipos “Restricciones de Similitud basadas en Vocabulario” (VS) y “Restricciones de Similitud basadas en Dependencias” (DS).

El tipo VS se encarga de definir si dos actores tienen vocabularios similares. Es utilizado para definir la similitud semántica. El proceso para calcularla consiste en tokenizar los nombres de los métodos, campos, variables, parámetros, tipos, etc., preprocesando indicadores con la técnica Camel Case Splitter. Luego, se procesa la información obtenida utilizando la técnica de similitud del coseno, representando a ambos actores como un vector n dimensional, donde cada dimensión corresponde a un término del vocabulario. Por ejemplo, para dos actores C1

y C2, la ecuación sería la siguiente:

Donde y son los vectores n dimensionales, y los pesos wi se calculan previamente.

(24)

24

 Llamadas compartidas a métodos (SMC): Se calcula utilizando un gráfico de llamadas, cuyos nodos representan métodos y sus aristas representan llamadas entre ellos (desde y hacia). Se considera la relación entre dos actores analizando los vecinos de cada uno de ellos y viendo la relación existente. Se maneja tanto para llamadas entrantes como salientes. Si se toman dos actores llamados C1 y C2 (clases) las

ecuaciones que defines esta regla son las siguientes:

Se calcula esta métrica usando el promedio entre las dos ecuaciones.

 Accesos compartidos a campos (SFA): Se calcula capturando todas las referencias de los campos usando análisis estático en todo el cuerpo de un método para identificar dependencias basadas en accesos a los campos (lectura o modificación). Si se consideran nuevamente los actores C1 y C2, y la función fieldRW(Ci) como aquella que retorna los campos leídos y modificados por la clase Ci, la forma de calcular esta

regla es la siguiente:

(25)

25

Tabla 3.1: Selección de refactorings basado en reglas de MORE

El cruce final es definido por el algoritmo genético NSGA-III, y se calcula la cantidad de code smells resueltos con la siguiente ecuación (no hace un cálculo específico para Feature Envy):

3.2.1.3 c-JRefRec

La herramienta c-JRecRef [42] es una herramienta de recomendación de oportunidades para la aplicación del refactoring Move Method.

El enfoque de la misma tiene como principal objetivo tener en cuenta la semántica del código, además del análisis de métricas para medir la efectividad de las recomendaciones. Adicionalmente, se encarga de detectar las oportunidades de refactoring a medida que el programador compila su código, basándose en los cambios introducidos por el mismo.

En primera instancia, utiliza el AST Parser del Eclipse Java Development Tools (JDT) para analizar las relaciones entre clases y métodos. En base a ellas, realiza un grafo direccionado de dependencias G = (V, E) donde los vértices V representan los métodos y campos de un programa y las aristas E representan dependencias (llamados a métodos y acceso a campos) entre ellos. Adicionalmente, obtiene la información semántica utilizada para la recomendación extrayendo todos los identificadores, incluyendo nombres de paquetes, clases, métodos, atributos, y parámetros para cada clase.

(26)

26

Figura 3.3: Vista Class State View de la herramienta c-JRefRec

Para cada clase, provee 4 columnas con información relevante al análisis de oportunidades de refactoring:

1. methods(C): Indica la cantidad de métodos definidos en la clase C. 2. edges(C): Indica la cantidad de aristas entrantes y salientes de la clase C.

3. clients(C): Indica la cantidad de clases que utilizan métodos o campos de la clase C. 4. dependents(C): Indica la cantidad de métodos o campos de otras clases accedidos

por la clase C.

Esta vista se refresca automáticamente al guardar/compilar el código fuente, y muestra en rojo los cambios introducidos para cada indicador en relación al último estado guardado.

Adicionalmente, la herramienta provee una segunda vista llamada Refactoring Candidates View (Figura 3.4) la cual presenta el método candidato a ser movido, la clase a la cual debería ser movido y las métricas relacionadas para ayudar al desarrollador a tomar la decisión.

Figura 3.4: Vista Refactoring Candidates View de la herramienta c-JRecRef

(27)

27

La similitud semántica entre un método m y una clase c se calcula mediante la fórmula SS(m, c) = cos(tf -idf(m),tf -idf(c)) utilizando los vectores tf-idf donde los métodos y las clases son considerados como documentos individuales.

El análisis de dependencias se realiza teniendo en cuenta 3 factores:

1. Δedges(R, C): Es el número de aristas agregados/eliminados de la clase C al aplicar el

refactoring R.

2. Δclients(R, C): Es el número de clases clientes agregadas/eliminadas al aplicar el refactoring R.

3. Δdependents(R, C): Es el número de dependencias agregadas/eliminadas al aplicar el refactoring R.

En base a estos 3 indicadores y a la semántica, se definen las oportunidades de aplicación de Move Method utilizando la siguiente función:

Δedges(R,Coriginal) + Δedges(R,Ctarget) + Δclients(R,Coriginal) + Δclients(R,Ctarget) +

Δdependents(R,Coriginal) + Δdependents(R,Ctarget) < 0 AND SS(m, Coriginal) < SS(m,Ctarget).

Con la presentación de las clases candidatas, concluye el flujo de c-JRecRef, y queda como responsabilidad para el programador la aplicación o no de los refactorings recomendados.

3.2.1.4 JMove

La herramienta JMove [42] es un recomendador de oportunidades para la aplicación del refactoring Move Method. Tiene la particularidad de ser más simple que las herramientas anteriormente analizadas, y de no analizar en ningún momento el impacto de sus recomendaciones sobre el code smell Feature Envy.

JMove utiliza un coeficiente de similitud, de esta forma, si identifica un método más similar a una clase que a la clase en la que se encuentra, recomienda la aplicación del refactoring. El esquema general del algoritmo utilizado consiste en la comparación entre la similitud de un método f, la clase C en la que se encuentra y la similitud de dicho método con las demás clases Cidel sistema:

Para lograrlo, la solución se encuentra implementada en 3 módulos:

(28)

28

Módulo de cálculo de similitud: Para definir la similitud entre dos métodos f’ y f’’ se utiliza la siguiente función:

donde a es el número de dependencias comunes a los 2 métodos, b es el número de dependencias que únicamente se encuentran en el método f' y c es el número de dependencias que únicamente se encuentran en el método f’’. S(f’,f’’) = 1 indica similitud máxima y S(f’,f’’) = 0 indica que los métodos no tienen dependencias en común. La forma que utiliza la herramienta para detectar la similitud entre un método f y una clase C consiste en calcular la media aritmética de la similitud entre f y todos los métodos de C.

Módulo de recomendación: Finalmente, dado el conjunto de clases T, la función best_class(f, T) indica la clase más apropiada, es decir, la que posee mayor similitud con el método.

Como se puede ver en la Figura 3.5, una vez realizado el análisis, JMove presenta los resultados en la interfaz de Eclipse, indicando la clase candidata a refactorizar y la clase destino propuesta por el recomendador.

Figura 3.5: Interfaz de JMove

3.2.2 Herramientas de aplicación de refactorings

(29)

29

3.2.2.1 Eclipse

La API nativa de Eclipse, posee la capacidad de realizar el refactoring Move Method. Es importante aclarar que no hace ninguna recomendación, simplemente realiza la operación on-demand.

Al intentar realizar un Move Method, en primera instancia analiza un conjunto de precondiciones para garantizar la no modificación del comportamiento original del sistema y la no generación de errores de sintaxis. Entre las más comunes, se encuentran la no existencia de llamados recursivos y la comprobación de que el método no esté definiendo un método abstracto en la jerarquía de herencias.

Posteriormente, Eclipse presenta las clases hacia las cuales es posible mover el método. Únicamente tiene en cuenta las clases de los parámetros del mismo, y de variables de instancia usadas. En caso de no existir parametrización o uso de variables de instancia, no se permite realizar el Move Method al no tener candidatos.

3.2.3 Herramientas de recomendación y aplicación de

refactorings

En esta sección se encuentran las herramientas que realizan por completo el proceso de eliminación de code smells, es decir, se encargan de recomendar posibles refactorings y aplicarlos en caso de ser seleccionados. Son las únicas que automatizan por completo el proceso de refactorización de Feature Envy.

3.2.3.1 JDeodorant

JDeodorant [25] es la única herramienta que actualmente se encarga de identificar, recomendar y aplicar el refactoring Move Method para la resolución del code smell Feature Envy. A diferencia de los recomendadores analizados anteriormente (sección 3.2.1), este plugin de Eclipse no realiza un complejo análisis para la recomendación, simplemente utiliza el ASTParser del Eclipse Java Development Tool (JDT) para analizar las relaciones entre entidades y aplicar el refactoring.

El verdadero aporte de esta herramienta es la aplicación correcta del refactoring Move Method al realizar una completa pre-evaluación de todos los posibles impactos sobre la calidad del diseño del sistema y aplicar la más efectiva.

(30)

30

Figura 3.6: Presentación de Feature Envy de JDeodorant

Como se puede ver, la herramienta propone un ranking de refactorings a aplicar. Para completar el proceso, se puede seleccionar un refactoring propuesto y presionar el botón “Apply Refactoring” para implementar la solución sugerida.

Al realizar un análisis del código interno de JDeodorant, se puede apreciar que la comprobación de pre-condiciones para realizar un Move Method es extremadamente compleja y tiene en cuenta todos los inconvenientes posibles que podrían imposibilitar su aplicación.

Finalmente, se ha comprobado que la recomendación y aplicación se condice con la propuesta por diferentes autores, por los que esta herramienta, a nivel de refactoring de Feature Envy, se considera correcta y de alta utilidad.

3.3 Criterios de comparación

En esta sección se definen los criterios para realizar la comparación entre las distintas herramientas analizadas en la sección 3.2.

 Categoría de la herramienta: se categoriza cada herramienta según la etapa de refactoring que soluciona.

 Una vez categorizada, se tienen en cuenta los siguientes criterios:

o Solución de Feature Envy: Define si la herramienta intenta solucionar el code smell o no.

o Tipo: Define si la herramienta requiere asistencia del programador en alguna de sus fases (automática o semi-automática)

(31)

31

3.3.1 Comparación y conclusiones

En la Tabla 3.2 se muestra cada categoría y la etapa de refactoring que intenta solucionar. La mayoría de las herramientas disponibles se centran en la recomendación del refactoring Move Method, debido a que existe muy poco soporte en la API de Eclipse para su implementación. Únicamente JDeodorant aplica automáticamente este refactoring al realizar una compleja implementación del análisis completo de pre-condiciones que se deben cumplir para poder realizar el refactoring. Sin embargo, esta herramienta no se centra en un análisis complejo para la recomendación, por lo que en muchas ocasiones no provee una recomendación óptima para aplicar el refactoring Move Method.

Herramienta Enfoque de recomendación de Move Method Enfoque de aplicación de Move Method Enfoque de recomendación y aplicación de Move Method

Sección

JDeodorant X 3.2.3

JMove X 3.2.1

MORE X 3.2.1

MethodBook X 3.2.1

c-JRecRef X 3.2.1

Nativa del IDE Eclipse X 3.2.2

Tabla 3.2: Categorías de las herramientas

En la Tabla 3.3 se pueden ver las herramientas junto con las prestaciones que proveen. Se pueden encontrar datos sobre el enfoque de solución de Feature Envy, la participación del desarrollador en el proceso de refactoring y los errores que introducen las herramientas al realizar el refactoring.

Herramienta Soluciona Feature Envy

Automática / Semi - Automática

Errores post refactoring

JDeodorant Sí Automática Sí

JMove No Semi-Automática No

MORE No Semi-Automática No

MethodBook No Semi-Automática No

c-JRecRef No Semi-Automática No

Nativa del IDE Eclipse No Semi-Automática No

Tabla 3.3: Características de las herramientas

(32)

32

mover manualmente el método seleccionado y realizar el control de dependencias, asegurándose de no modificar el comportamiento del sistema. Es por esto que las demás herramientas se consideran semi-automáticas, debido a que no pueden completar el proceso sin la intervención del desarrollador.

La columna de errores post-refactoring muestra cuales son las herramientas que introducen errores al aplicar el refactoring. JDeodorant es la única que lo hace debido a que es la única que aplica el refactoring luego de una recomendación. A pesar de poseer un completo control de pre-condiciones, se detectaron casos en los cuales genera errores detectables en tiempo de compilación luego de realizar un Move Method. Por su parte la API Nativa de Eclipse, a pesar de aplicar el refactoring, es más restrictiva y no permite la aplicación de Move Method si detecta que se va a introducir un error.

(33)

33

Capítulo 4: Un enfoque iterativo para la

refactorización de Feature Envy

4.1 Refactorización de Feature Envy

Es común que con el paso del tiempo se degrade la calidad de un sistema, debido a los cambios paulatinos que los desarrolladores deben introducir durante la evolución del software, ya que muchas veces se realizan sin el total conocimiento de la arquitectura del sistema y de sus requerimientos iniciales. El uso de malas prácticas, denominadas code smells, ha sido establecido como un concepto para identificar patrones o aspectos del diseño de software que pueden causar problemas para el desarrollo o el mantenimiento del sistema [32].

Uno de los 21 code smells catalogados por Lanza y Marinescu [3] es Featury Envy. Este code smell indica la existencia de métodos que parecen más interesados en los datos de otras clases que en lo de su propia clase. Estos métodos acceden directamente o vía métodos accesores a una gran cantidad de datos de otras clases [3]. Esto puede ser una señal de que el método está ubicado en un lugar incorrecto y debe ser movido a otra clase.

Es necesario refactorizar las Feature Envy para mejorar la cohesión y evitar los “efectos cascada” [3]. Es importante poner en una misma clase las cosas que cambiarán juntas [2]. Adicionalmente, la existencia de Feature Envy está relacionada directamente con la existencia de otros code smells como God Class o Data Class (Figura 4.1), por lo que es posible eliminar otros code smells al solucionar las Feature Envy de un sistema.

Figura 4.1 Correlación entre code smells. Lanza y Marinescu [3].

(34)

34

un método identificado como Feature Envy para definir si es posible mover el mismo o un fragmento del mismo a una clase candidata, y luego la aplicación automática de dichas acciones. Estas acciones conllevarán a la eliminación del code smell, aumentando la modificabilidad y extensibilidad del sistema. Además, se permite la visualización de las soluciones generadas para que el desarrollador pueda analizarlas, observando las métricas propuestas, y así encontrar la más adecuada para luego aplicar al proyecto.

En la resolución del code smell Feature Envy intervienen 2 refactorings diferentes: Extract Method para obtener el fragmento de código y Move Method para moverlo a la clase a la que debería pertenecer. Estos refactorings no son triviales debido al conjunto de precondiciones a tener en consideración para no modificar la funcionalidad del sistema. Por ejemplo, para el refactoring Move Method, se deben tener en cuenta un conjunto de precondiciones para definir si es posible la aplicación del mismo

1. La clase objetivo no debe heredar un método que posea el mismo nombre que el método a mover.

2. El método a ser movido no debe sobreescribir un método heredado en su clase original.

3. El método a ser movido no debe ser synchronized.

4. El método a ser movido no debe contener asignaciones a campos de la clase origen (incluyendo campos heredados).

5. El método a ser movido debe tener una relación uno a uno con la clase objetivo.

Es importante tener en cuenta estos factores para la aplicación del enfoque propuesto.

4.2 Esquema general de la solución iterativa de Feature Envy

El enfoque para la refactorización sigue un ciclo iterativo donde la entrada es una Feature Envy y la salida es un conjunto de soluciones a la misma en caso de existir. Estas soluciones son el conjunto de refactorings realizados junto con sus métricas asociadas.

Este enfoque consta de 6 actividades, aunque dependiendo las características del método, puede requerir actividades adicionales para realizar extracciones.

1. Obtener características del método: Esta actividad recibe como entrada un método el cual ha sido identificado previamente como Feature Envy. El objetivo de esta actividad es recuperar las características específicas del método que ayudarán a decidir cuál será la mejor forma de refactorizarlo.

(35)

35

3. Seleccionar el fragmento de código a mover: En esta actividad se decide cuál es el fragmento del método que no corresponde a la clase en la que se encuentra. Utilizando la información obtenida en el paso 1, se puede decidir por no extraer ningún fragmento del método y moverlo completamente. Finalmente, se aplican los refactorings seleccionados solucionando de esta manera la Feature Envy.

4. Calcular métricas de la solución generada: Una vez encontrada la solución, se procede con esta actividad, que consiste en calcular las métricas del código pertinentes a la existencia de Feature Envy. De esta forma, el desarrollador puede evaluar la calidad de la solución y definir si le parecen aceptables los valores obtenidos.

Como se ve en la Figura 4.2, el proceso itera en las actividades 3 y 4 hasta encontrar soluciones o hasta agotar las clases que pueden ser consideradas como candidatas para realizar el refactoring.

Figura 4.2: Esquema general de la solución de Feature Envy.

(36)

36

 No se encontró solución a la Feature Envy.

 El programador no desea aplicar ninguna solución propuesta.

 El programador elige una solución.

Los primeros dos escenarios conllevan a la finalización del flujo de ejecución, dando como resultado la no refactorización del code smell Feature Envy. El tercer caso permite continuar el flujo con las últimas dos actividades:

5. Aplicar el refactoring: Cuando el desarrollador selecciona la solución que considera más óptima, se procede a la aplicación efectiva de los refactorings que generan dicha solución.

6. Corregir errores: Esta actividad es la única que queda completamente a cargo del desarrollador. Se debe verificar si el refactoring Move Method ha introducido algún error de código detectable en tiempo de compilación y corregirlo.

En cada iteración, se deben priorizar de manera lo más precisa posible las clases candidatas, y seleccionar el mejor statement a extraer (o el método completo) para iterar la menor cantidad de veces posible. La selección de clase se prioriza utilizando la métrica ATDF, mientras que la selección de statement es dependiente de la heurística utilizada.

De esta forma, se itera hasta agotar las clases que pueden ser candidatas.

Para la aplicación de este enfoque, se optó por extender la herramienta Bandago [20] perteneciente al plugin JSpIRIT [13] de Eclipse. Por lo tanto, la solución presentada es aplicada para el lenguaje Java. JSpIRIT analiza código estáticamente y provee el método catalogado como Feature Envy que se utiliza de entrada. Bandago provee la estructura general para poder cumplir el ciclo de ejecución y presentar los resultados obtenidos de una forma sencilla.

4.3 Desarrollo del enfoque iterativo para la refactorización de

Feature Envy

A continuación se detalla cada actividad involucrada en el enfoque junto con un ejemplo guía. Este ejemplo consiste en un método afectado por el code smell Feature Envy y un seguimiento de cada uno de los cambios realizados por cada actividad sobre el código Java del mismo.

4.3.1 Actividad 1: Obtener características del método

(37)

37

Figura 4.3: JSpIRIT Smells View

Como se puede ver en la Figura 4.3, se identifican diferentes tipos de code smells, y es el usuario el que debe seleccionar la Feature Envy que servirá como entrada para la refactorización. Para realizar esta acción, se utilizará la interfaz de la extensión de JSpIRIT, llamada Bandago, la cual permite seleccionar un método afectado por un code smell y aplicar un refactoring en caso de ser posible.

En esta actividad, se identifican todos los llamados a métodos propios y uso de variables de instancia de la clase origen. En la Figura 4.4 se muestra la identificación variables de instancia.

Figura 4.4: Llamados a variables de instancia de la propia clase del método ejemplo.

(38)

38

están utilizando variables de instancia cuando dicha métrica es mayor a cero. Esta métrica se calcula utilizando la siguiente fórmula:

Para el ejemplo presentado, el número de accesos a variables de instancia es 3, debido a que se contabilizan los 3 accesos señalados en la Figura 4.4. En cuanto al número total de accesos, se deben sumar los 3 accesos a variables de instancia y todos los accesos a parámetros y variables locales del método. En este caso, se deben sumar todos los accesos a los parámetros “e” y “oldEntry”, dando como resultado un total de 19 accesos. De esta forma, al reemplazar en la ecuación presentada, puede observarse que la métrica LAA posee un valor mayor a 0:

El análisis de esta información para definir el refactoring a utilizar se realizará posteriormente en la actividad 3.

4.3.2 Actividad 2: Elegir la clase candidata

Una vez identificadas las características del método, se procede a identificar la clase candidata a la cual se debe mover el método. Con este objetivo, nuestro enfoque utiliza la métrica Access to Foreign Data (cantidad de atributos de otras clases que utiliza el método) debido a que se encuentra directamente relacionada con la existencia de Feature Envy. La misma se obtiene calculando el número de llamados directos o indirectos (vía métodos getters y setters) a campos de clases externas.

(39)

39

Figura 4.5 Identificación de llamados a clases externas

La Figura 4.5 muestra la identificación de llamados a clases externas involucradas en el ejemplo presentado. Para obtener el valor de la métrica ATFD se debe contabilizar el total de dichos llamados, por lo cual el valor de la misma para el ejemplo presentado es 17. Se puede observar que se debió detallar el path completo de las clases externas que intervienen debido a que poseen el mismo nombre. Para definir las clases que pueden ser consideradas candidatas, se calcula el valor de la métrica ATFD para cada una de las clases extranjeras involucradas.

Ranking Nombre de la clase Nombre del parámetro

Valor ATDF

1 name.gyger.jmoney.model.Entry e 9

2 net.sf.jmoney.model.Entry oldEntry 8

Tabla 4.1: Priorización de clases externas accedidas por la Feature Envy

Como se detalla en la Tabla 4.1, se considera la clase name.gyger.jmoney.model.Entry como la clase más envidiada (primera en el ranking) al ser la que recibe la mayor cantidad de llamados por parte del método catalogado como Feature Envy. Al analizar el valor de ATDF, se puede observar que la cantidad de llamados a net.sf.jmoney.model.Entry también es significativa.

4.3.3 Actividad 3: Seleccionar el fragmento de código a mover

En esta actividad se utiliza la información obtenida en la actividad 1 (Sección 4.3.1) para definir cuál es el fragmento del método que no corresponde a la clase en la que se encuentra. Es decir, se analizarán las características propias del método para tomar la decisión.

(40)

40

de variables de instancia de la clase en la cual se encuentra el método generen una Feature Envy al encontrarse en una nueva clase.

En caso de no utilizar ninguno, se considera que el método puede ser movido completamente a la clase candidata seleccionada en la actividad anterior (Sección 4.3.2) y la actividad actual finaliza.

En caso de existir algún acceso a variables de instancia de la clase, se debe seleccionar el fragmento de código del método que al ser movida de la clase original, elimina la existencia de la Feature Envy identificada sin generar este code smell en la nueva clase. Para lograr este objetivo, se optó por la utilización de una heurística que consta de dos pasos:

1. Identificar la primera y última ocurrencia de un llamado a un método o atributo de la clase candidata.

2. Seleccionar el statement o conjunto de statements más pequeño que contiene ambos llamados. Este paso evita la rotura de flujos de control, por ejemplo, cortar una sentencia IF a la mitad.

Figura 4.6: Selección de statement o conjunto de statements a extraer

Como se ve en la Figura 4.6, estos pasos se repiten para cada clase candidata identificada.

(41)

41

Figura 4.7: Selección del statement candidato a ser extraído para la clase name.gyger.jmoney.model.Entry

Como se puede observar, en este caso la primera y última ocurrencia no se encuentran en ningún flujo de control, por lo cual es posible seleccionar un statement que comience en la primera ocurrencia y termine en la última. Otro factor importante es la existencia de una variable de instancia de la clase original en dicho statement, pero se puede corroborar analizando la métrica LAA (Locality of Atribute Acceses) que la relación entre el uso de variables locales y llamados externos no es significativa. Su cálculo se obtiene mediante la siguiente ecuación:

Para el statement del ejemplo, el número de accesos a variables de instancia es 1, ya que únicamente se realiza un acceso a la variable de instancia entryStates. En cuanto al número total de accesos, se incluye el acceso antes mencionado y todos los llamados a métodos de los parámetros e y oldEntry, dando como resultado un total de 16 accesos.

De esta forma, se visualiza que el statement seleccionado se sigue encontrando en los límites establecidos por Lanza y Marinescu [3] (LAA < ONE THIRD), y se procede a seleccionarlo como candidato a ser extraído.

4.3.4 Actividad 4: Calcular las métricas de la solución generada

(42)

42

Las métricas permiten al desarrollador evaluar la calidad de cada solución. Las métricas calculadas son las tres asociadas a la existencia de Feature Envy (ATFD, LAA y FDP). La métrica Access to Foreign Data (ATFD) indica el uso directo de atributos de otras clases. La métrica Locality of Attribute Accesses(LAA) indica la relación entre el acceso a atributos locales y el acceso a atributos extranjeros. Finalmente, la métrica Foreign Data Providers (FDP) indica la cantidad de clases que proveen atributos extranjeros al método.

En la Figura 4.8 se observa el cálculo de las métricas de la solución generada. Al comparar los valores del método original con los de la solución propuesta, se puede observar que la métrica ATDF disminuyó su valor considerablemente. Esto se debe a que una gran parte de los accesos a clases extranjeras (los cuales se contabilizan para obtener el valor de esta métrica) pertenecen a la clase a la cual se moverá el método, por lo que dichos llamados dejarán de ser extranjeros y pasarán a ser locales a la clase en la que se encuentran. Por esta misma razón, la relación entre número de accesos locales y número total de accesos aumenta, dando como resultado un valor más elevado al calcular la métrica LAA. El valor de la métrica FDP disminuye al reducirse el número de clases extranjeras utilizadas por el método.

Figura 4.8: Cálculo de las métricas de la solución generada (Bandago View)

El análisis que debe realizar el desarrollador debería basarse en los siguientes conceptos:

ATFD: Se debe considerar positiva la disminución del valor de esta métrica. Esto implicaría que la solución accede a una menor cantidad de datos de clases externas.

LAA: Se debe considerar positivo el aumento del valor de esta métrica. Esto implicaría que la solución utiliza un mayor porcentaje de variables propias en relación al uso total de variables.

FDP: El aumento significativo en el valor de esta métrica implica la eliminación de la Feature Envy. Esto se debe a que el uso de variables y métodos externos no se concentraría únicamente en un pequeño número de clases.

4.3.5 Actividad 5: Aplicar el refactoring

Esta actividad consiste en la aplicación efectiva de la solución seleccionada.

(43)

43

Figura 4.9: Selección de una solución para Feature Envy (Bandago Wizard)

De esta forma, el desarrollador puede ver aplicada la solución propuesta, en la cual se extrae el statement o conjunto de statements identificados por la actividad 3.

4.3.6 Actividad 6: Corregir errores

Esta actividad es la única que queda completamente a cargo del desarrollador. Se debe verificar que la aplicación del refactoring Move Method no haya introducido errores de código detectables en tiempo de compilación.

Figura 4.10: Método movido a la clase candidata

Referencias

Documento similar