• No se han encontrado resultados

Comparación de dos herramientas para refactorización de brain methods

N/A
N/A
Protected

Academic year: 2020

Share "Comparación de dos herramientas para refactorización de brain methods"

Copied!
138
0
0

Texto completo

(1)

TESIS DE GRADO EN INGENIERÍA DE SISTEMAS

COMPARACIÓN DE DOS HERRAMIENTAS PARA

REFACTORIZACIÓN DE

BRAIN METHODS

Por

Scafati, Diego Ariel

Director: Diaz-Pace, Andres Co-Director: Vidal, Santiago A.

FACULTAD DE CIENCIAS EXACTAS

UNIVERSIDAD NACIONAL DEL CENTRO DE LA PROVINCIA DE BUENOS AIRES

(2)

Índice

Índice ... 1

Índice de Figuras ... 3

Índice de Códigos fuente ... 4

Índice de Tablas ... 4

Índice de Fórmulas ... 4

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

1.1 Motivación ... 5

1.2 Objetivos ... 6

1.3 Estructura de la tesis ... 8

Capítulo 2: Marco Teórico ... 9

2.1 Calidad de software ... 9

2.1.1 Definición de calidad de software ... 9

2.1.2 Mantenibilidad ... 9

2.2 Evolución y mantenimiento de sistemas ... 11

2.2.1 Definición de evolución y mantenimiento de software ... 11

2.2.2 Problemas en la evolución y mantenimiento de software ... 11

2.2.3 Código legado ... 12

2.2.4 Soluciones propuestas ... 12

2.3 Code Smell ... 13

2.3.1 Definición de Code Smell ... 13

2.3.2 Impacto de los Code Smells ... 13

2.3.3 Surgimiento de Code Smells ... 13

2.3.4 Identificación de Code Smells ... 14

2.3.4.1 Métricas ... 14

2.3.4.2 Umbrales ... 14

2.3.4.3 Utilidad de las métricas ... 15

2.3.5 Long Method y Brain Method ... 15

2.4 Refactoring ... 18

2.4.1 Definición ... 18

2.4.2 Aplicación de refactorizaciones ... 19

2.4.3 Extract Method ... 19

2.4.4 Problemas de la refactorización de código ... 20

2.4.5 Minimización de riesgos ... 20

(3)

3.1 Automatización del refactoring mediante herramientas de software ... 21

3.1.1 Descomposición del proceso de refactoring ... 21

3.2 Trabajos relacionados ... 21

3.2.1 Automed ... 21

3.2.2 PMD ... 22

3.2.3 Checkstyle ... 23

3.2.4 Herramientas integradas en entornos de desarrollo ... 23

3.2.5 JSpIRIT y Bandago ... 24

3.2.5.1 JSpIRIT ... 24

3.2.5.2 Bandago ... 24

3.2.5.2.1 Simulated Annealing ... 25

3.2.6 JDeodorant ... 27

3.2.6.1 Program Slicing ... 28

3.2.7 Benefactor... 31

3.2.8 Comparación de las herramientas disponibles ... 31

3.2.9 Trabajos relacionados sobre la comparación de la efectividad de las herramientas ... 34

3.3 Conclusiones ... 35

Capítulo 4: Experimento #1 - Comparación inicial entre Bandago y JDeodorant ... 36

4.1 Estructura del experimento ... 36

4.2 Resultados ... 38

4.3 Discusión ... 43

Capítulo 5: Extensiones de Bandago y de JDeodorant ... 45

5.1 Extension en JDeodorant ... 45

5.1.1 Simulated Annealing para la solución automática de Long Methods ... 45

5.1.2 Búsqueda con Backtracking ... 48

5.1.3 Búsqueda con Backtracking sobre Brain Methods ... 50

5.2 Extensión en Bandago ... 53

Capítulo 6: Experimento #2 - Comparación de las Herramientas ... 56

6.1 Estructura del experimento ... 56

6.2 Resultados ... 58

6.3 Conclusión ... 63

Capítulo 7: Conclusiones ... 64

7.1 Contribuciones... 64

7.2 Limitaciones ... 65

(4)

Referencias ... 67

Anexos ... 71

Anexo 1: Resultados de la ejecución manual de la herramienta JDeodorant sin

modificaciones, por cada método en cada uno de los 10 proyectos. ... 72

Anexo 2: Resultados de la ejecución automática de la herramienta JDeodorant post

modificaciones, por cada clase en cada uno de los 10 proyectos. ... 86

Anexo 3: Resultados de la ejecución automática de la herramienta Bandago post

modificaciones, utilizando el operador Random, por cada clase en cada uno de los 10

proyectos. ... 97

Índice de Figuras

Figura 1.1 Diagrama de contexto del funcionamiento de la herramienta JDeodorant. ... 7 Figura 1.2 Diagrama de contexto del funcionamiento de la herramienta Bandago. ... 7 Figura 1.3 Diagrama de contexto del funcionamiento de la herramienta JDeodorant

(5)

Figura 6.3 Cantidad de Brain Methods corregidos por JDeodorant y Bandago extendidos, por proyecto, expresada en porcentajes. ... 60 Figura 6.4 Cantidad de Brain Methods corregidos por JDeodorant y Bandago extendidos, expresada en porcentajes. ... 60 Figura 6.5 Tiempos de ejecución de la herramienta JDeodorant extendida. ... 61 Figura 6.6Tiempos de ejecución de la herramienta Bandago extendida. ... 62

Índice de Códigos fuente

Código Fuente 2.1 Ejemplo de Long Method ... 17

Código Fuente 3.1 Pseudocódigo para el algoritmo de Simulated Annealing implementado

en Bandago. ... 26 Código Fuente 5.1 Pseudocódigo para la generación de estados vecinos ... 47

Código Fuente 5.2 Pseudocódigo para la función de Backtracking ... 50

Código Fuente 5.3 Pseudocódigo para un operador soportando conjuntos de sentencias no contiguas. ... 55

Índice de Tablas

Tabla 3.1 Clasificación de las herramientas de acuerdo a su enfoque. ... 32 Tabla 3.2 Comparación de las herramientas de acuerdo a su disponibilidad. ... 33 Tabla 3.3 Comparación de las herramientas de acuerdo al enfoque de identificación, nivel de automatización, y validez de las extracciones. ... 34 Tabla 4.1 Detalle de las herramientas analizadas. ... 37 Tabla 5.1Ejemplo de los valores para las métricas de los primeros 4 estados analizados en la clase CalendarImporter del proyecto Columba. ... 52 Tabla 5.2 Ejemplo de los valores normalizados para las métricas de los primeros 4 estados analizados en la clase CalendarImporter del proyecto Columba. ... 52 Tabla 6.1 Comparación de los tiempos de ejecución de JDeodorant y Bandago antes y después de ser extendido. ... 63

Índice de Fórmulas

Fórmula 2.1 Definición de la métrica LOC mediante la utilización de umbrales. ... 17 Fórmula 2.2 Composición de Métricas para definir el Code Smell de Tipo Brain Method. ... 18 Fórmula 3.1 Cálculo del beneficio de una extracción. ... 22 Fórmula 5.1 Definición de la función para determinar el mejor estado parcialmente

(6)

Capítulo 1: Introducción

La tarea de desarrollar software de calidad para grandes sistemas conlleva problemas y desafíos, a pesar de los numerosos esfuerzos realizados para mejorar la habilidad de las personas para producir software de forma predecible y eficiente en términos de costos [1]. En general, estos problemas se deben a la inevitable evolución de los sistemas de software, causada por cambios en el entorno del sistema. Estos cambios incluyen, por ejemplo, cambios de requerimientos, cambios de tecnología, y cambios de personal, entre otros. Esta evolución lleva a la necesidad de efectuar un proceso de mantenimiento que modifica el código fuente del software.

La evolución del software fue estudiada por medio de “leyes” en el trabajo de Lehman [2, 3], las cuales hacen hincapié en el hecho de que el software debe evolucionar continuamente para seguir siendo útil, y que dicha evolución viene acompañada de un aumento de la complejidad y del esfuerzo que tendrán que ser invertidos para controlar este crecimiento. Esto se debe a que, a medida que los desarrolladores realizan modificaciones en el código de un sistema, el código tiende a perder su estructura y se hace más difícil comprender el diseño mediante la simple lectura del código. Esto ocurre principalmente cuando los cambios se realizan sin una plena comprensión del diseño del código. La pérdida de la estructura del código tiene un efecto acumulativo: cuanto más difícil se hace ver el diseño en él, más difícil es preservarlo, y más rápidamente dicho diseño tiende a erosionarse [4].

Entre los problemas que afectan la comprensibilidad del código y su diseño se encuentran: la duplicación de código y los métodos excesivamente largos (en términos de líneas de código o cantidad de sentencias). Estos dos problemas fueron identificados por Fowler y Beck [5, 6] como problemas que indican la necesidad de refactorizar una pieza de software, para mejorar su calidad y evitar la degradación del sistema.

1.1 Motivación

Una pregunta importante es cómo se puede controlar la complejidad del software. Una respuesta es a través del diseño orientado a soportar el cambio, el cuál abarca: técnicas de

separación de concerns, ocultamiento de información, uso de mecanismos de herencia y

polimorfismo, separación del diseño en capas, mantenimiento actualizado de la documentación, y desarrollo orientado a pruebas, entre otras estrategias.

Una estrategia común es la reingeniería de software, en la cual se modifica un sistema existente para mejorar su diseño y reducir la complejidad. Uno de los enfoques más utilizados consiste en la identificación de potenciales problemas de software por medio de los llamados

Code Smells”. Un Code Smell es un conjunto de características presentes en el código, las

cuales pueden cuantificarse mediante métricas, y sugieren la existencia de un problema de

diseño en el código. Para eliminar estos Code Smells, se han identificado técnicas de

refactorización de código (refactoring) las cuales están normalmente asociadas a cada Code

Smell en particular. Estas técnicas consisten en distintas formas de modificar el código fuente

(7)

Varias herramientas existentes (por ej., JDeodorant, Bandago, AutoMed, entre otras) asisten a los desarrolladores en diferentes partes del proceso de refactorización, aunque no proveen una automatización completa de dicho proceso. Esta automatización es ideal y necesaria, dado que a menudo resulta inviable o poco práctico realizar refactorizaciones de forma manual, principalmente en grandes bases de código, por el tiempo que esto demora y por la probabilidad de introducir nuevos problemas o realizar accidentalmente refactorizaciones

inválidas. En particular, las herramientas existentes suelen enfocarse solamente en el Code

Smell de tipo Long Method y no así en el Code Smell Brain Method, el cual se define como

como una extensión del primero e indica problemas de forma más precisa.

1.2 Objetivos

El objetivo de este trabajo es realizar un estudio sobre dos herramientas relevantes de la

literatura para refactorización de Code Smells. Estas son las herramientas JDeodorant y

Bandago [9, 10].

La herramienta Bandago automatiza la mayor parte del proceso de refactorización, desde la

detección de Code Smells hasta la eliminación de los mismos vía refactoring. Adicionalmente,

Bandago se enfoca en el Code Smell Brain Method, el cual es más específico que el Code

Smell Long Method y suele indicar problemas más complejos. Por otro lado, la herramienta

JDeodorant proporciona muy buenos resultados con respecto a otras herramientas de

eliminación de Long Methods [59]. Esto se debe a su enfoque de detección de oportunidades

de refactorización basado en la técnica conocida como Program Slicing [52]. Una limitación

de JDeodorant es su bajo nivel de automatización.

La idea de este trabajo es realizar una comparación entre Bandago y JDeodorant para poder determinar cuál de los dos enfoques es más efectivo, y realizar las modificaciones necesarias en ambas herramientas para lograr la automatización del proceso de refactorización de las

mismas, enfocado en la eliminación de Brain Methods. Con esto, se busca crear una

herramienta capaz de eliminar dichos Code Smells de forma automática y de la manera más

eficiente y eficaz posible.

Para realizar esta comparación, se analizaron 10 proyectos Java de código abierto. Sobre estos proyectos se ejecutaron las herramientas JDeodorant, JSpirit y Bandago para obtener

métricas relacionadas a la cantidad de Long Methods y Brain Methods presentes en el código,

cantidad de Long Methods y Brain Methods eliminados luego de la ejecución de las

herramientas, y también a los tiempos de ejecución de estas herramientas o cantidad de pasos manuales realizados en el caso de JDeodorant. En base a estos resultados, se estudió el diseño interno de las herramientas Bandago y JDeodorant y se realizaron modificaciones en pos de lograr una comparación más pareja entre ambas. Estas modificaciones incluyen el

agregado de una etapa de detección de Brain Methods en JDeodorant, la implementación de

un algoritmo para automatizar la ejecución manual en JDeodorant, y la implementación del

algoritmo de Program Slicing en la herramienta Bandago más una función de toma de

(8)

En las Figura 1.1 y 1.2 se observa el funcionamiento de las herramientas JDeodorant y Bandago desde el punto de vista del desarrollador durante el proceso de mantenimiento de un sistema. En ambos casos se observa la falta de automatización total del proceso.

Figura 1.1 Diagrama de contexto del funcionamiento de la herramienta JDeodorant.

Figura 1.2 Diagrama de contexto del funcionamiento de la herramienta Bandago.

En las Figuras 1.3 y 1.4 se observa el funcionamiento de las herramientas JDeodorant y Bandago desde el punto de vista del desarrollador, durante el procseo de mantenimiento de un sistema, luego de aplicar las mejoras mencionadas en ambas herramientas. Se destaca la automatización total del proceso.

(9)

Figura 1.4 Diagrama de contexto del funcionamiento de la herramienta Bandago mejorada.

1.3 Estructura de la tesis

El esquema general del trabajo final está organizado de la siguiente manera.

En el capítulo 2 se explica de manera más detallada el marco teórico que permitirá comprender el resto del trabajo aquí realizado. Luego en el capítulo 3 se incluye una descripción del estado del arte detallando particularmente las dos herramientas que mejor

intentan resolver el problema de la eliminación de Code Smells, y que serán de gran ayuda

para tratar de alcanzar los objetivos de este trabajo (JDeodorant y Bandago).

En el capítulo 4 se muestra el primer análisis realizado sobre ambas herramientas en su estado original (sin realizar modificaciones aún). Este análisis busca comparar ambas herramientas para determinar cuál de ellas resulta más útil para resolver el problema que intentan solucionar y cuál de las dos resultaría más útil para alcanzar los objetivos de este

trabajo, el cual consiste en la eliminación automatizada de Code Smells de tipo Brain Method

en proyectos de software.

El capítulo 5 presenta una serie de mejoras realizadas sobre ambas herramientas, con el objetivo de poder realizar nuevamente un experimento que compare el enfoque de ambas herramientas en el contexto de aplicación perseguido en este trabajo.

En el capítulo 6 se muestran y analizan los resultados obtenidos de este nuevo experimento con el objetivo de evaluar ambas herramientas en el contexto de aplicación mencionado y poder determinar finalmente cuál de las dos herramientas logra solucionar el problema.

(10)

Capítulo 2: Marco Teórico

En este capítulo se definen y explican los conceptos asociados a calidad de software, evolución de sistemas de software, y mantenimiento de los mismos. Estos conceptos se encuentran relacionados estrechamente entre sí.

2.1 Calidad de software

2.1.1 Definición de calidad de software

La Organización Internacional para la Estandarización (ISO) define a la calidad, dentro del vocabulario para sistemas de administración de calidad (QMS), como el grado con el cual un conjunto de características inherentes de un objeto (producto, proceso, servicio, etc) satisface sus requerimientos [12].

La calidad del software se divide en dos importantes grupos según el objeto del que se trate, a saber: calidad de proceso y calidad de producto [1].

La calidad de producto refleja el carácter esencial, características, y propiedades de los artefactos de software, y es un reflejo de que tan bien soportan las necesidades de los stakeholders. Un stakeholder es un grupo, individuo, u organización que es afectada por, o puede afectar, un producto, proyecto u organización [1]. La calidad del producto es asociada generalmente a la ausencia de defectos en un artefacto de software. Sin embargo, esta calidad está relacionada también a otras propiedades, características y atributos que las personas valoran, como son: la disponibilidad, eficiencia, mantenibilidad, portabilidad, confiabilidad, reusabilidad, y usabilidad [1].

La calidad de proceso, por otro lado, refleja cómo es desarrollado un producto. Está relacionada con la forma en la que las personas desarrollan productos, y existen 3 atributos de calidad de proceso claves: eficacia (producción de productos listos para el servicio), eficiencia (aprovechamiento de recursos minimizando su gasto), y predictibilidad (estimación de recursos, costos y tiempos) [1].

2.1.2 Mantenibilidad

La mantenibilidad se define como el grado en el cual un artefacto de software facilita su modificación. Este término es muy amplio pero puede ser estudiado como 5 características individuales que componen la mantenibilidad, que son: extensibilidad, modularidad, simplicidad, testeabilidad y comprensibilidad [1]. Estas características se describen a continuación.

Extensibilidad: Es la medida en la cual un software puede ser expandido o mejorado. En el mundo real son varios los factores que cambian (por ej., necesidades de los clientes, tecnología, personal, etc.) por lo tanto esta característica debe ser planeada en el desarrollo de todo sistema de software no trivial [1].

(11)

sistemas modulares se descomponen en componentes más pequeños, cada uno resolviendo un fragmento de un problema más grande. La modularidad tiende a reducir defectos, reducir esfuerzos en trabajos de mantenimiento posterior, e incrementar la posibilidad de reutilizar módulos, asumiendo que estos tienen interfaces bien definidas y poco acoplamiento con otros módulos [1].

Simplicidad: Es la medida en la cual un sistema de software contiene solamente complejidad esencial. Así, los sistemas de software deberían incluir únicamente aquellas características que necesita para lograr su propósito. Agregar características no esenciales crea riesgos más grandes dado que estas no suelen encajar bien con el diseño general e incrementan su complejidad, reduciendo su prolijidad. Es decir, los sistemas de software deben tener integridad conceptual, lo que refleja un conjunto de ideas integradas que funcionan bien entre sí. Además, debido a la complejidad asociada con el desarrollo, la mayoría de los sistemas de software rápidamente agotan las limitaciones intelectuales de las personas, las mejores soluciones son generalmente más simples, cuestan menos en ser desarrolladas y tienen menos defectos [1].

Testeabilidad: Es la medida en que el software facilita la verificación de sus operaciones. Si la testeabilidad de un artefacto de software es alta, entonces es más fácil encontrar fallos en

el sistema (si los hay) por medio de pruebas o “tests”.

Comprensibilidad: Es la medida en que las personas pueden comprender el comportamiento de un sistema de software, que generalmente resulta en el desarrollo de sistemas más confiables. La comprensibilidad de los sistemas de software puede aumentarse, por ejemplo, mediante el uso de metodologías mejoradas de diseño y documentación [1].

(12)

2.2 Evolución y mantenimiento de sistemas

2.2.1 Definición de evolución y mantenimiento de software

El fenómeno de evolución de software [13] fue identificado por primera vez como tal a comienzo de los 70s [14,2]. Esta evolución es inevitable en proyectos de software exitosos y fue formulada por medio de 3 leyes en base a la información obtenida del estudio del proceso de desarrollo de software en un trabajo realizado por Lehman [2, 3]. Pese a que este análisis se basa en un estudio que data de 1968, aún se considera vigente, y se han llegado a identificar un total de 8 leyes.

Estas leyes hacen hincapié en el hecho de que el software debe evolucionar continuamente para seguir siendo útil, y que dicha evolución se acompaña de un aumento de la complejidad y el esfuerzo que tendrán que ser invertidos para controlar este crecimiento. La evolución de un software lleva a que el código deba ser modificado. Estos cambios pueden tener distintas razones, como por ejemplo: la corrección de errores, nuevas tecnologías, cambios en el hardware utilizado, mejoras en la eficiencia, nuevos requerimientos, etc.

La ley de cambio continuo [15] explica que la evolución se lleva a cabo en un proceso de mejora continua controlado y dirigido por feedback. Esta actividad es definida por ISO [16] como Mantenimiento:

La modificación del código y la documentación asociada de un producto de software debido a un problema o la necesidad de mejoras. El objetivo es modificar el producto de software existente preservando su integridad.

Similar es la definición dada por la IEEE [17]:

El proceso de modificar un sistema de software o un componente, después de que éste fue entregado, con el fin de corregir errores, mejorar la performance u otros atributos, o adaptarlo a un cambio en el ambiente.

2.2.2 Problemas en la evolución y mantenimiento de software

A medida que los desarrolladores modifican el código fuente (ya sean cambios para alcanzar objetivos a corto plazo, o cambios realizados sin una plena comprensión del diseño del código), el mismo pierde su estructura, y se hace más difícil ver el diseño mediante la lectura del código [4]. La pérdida de la estructura del código tiene un efecto acumulativo. Cuanto más difícil es ver el diseño en el código, más difícil es preservarlo, y más rápidamente se deteriora [4].

Este aumento de la complejidad es descrito en la regla de complejidad incremental [15] y estudiado por algunos autores bajo el nombre de fenómeno de envejecimiento, llamado así debido a la similitud con el proceso de envejecimiento humano. Si bien este fenómeno es una consecuencia del fenómeno de evolución, también puede surgir por la falta de mantenimiento.

(13)

Estas características de los proyectos de software han sido identificadas por la mayoría de las metodologías de desarrollo de software propuestas en las últimas décadas. En este contexto, ya no se considera el diseño como “grabado en piedra” sino que debe cambiar y ser revisado en distintas iteraciones debido a la llegada de nuevos requerimientos, y como parte del proceso de mantenimiento y mejoras para combatir el envejecimiento y degradación del sistema.

2.2.3 Código legado

El software legado (legacy software) es el software que fue heredado de un tercero o que existe dentro de la misma compañía. El hecho de ser heredado significa que el software puede ser anticuado, puede haber sido desarrollado utilizando lenguajes de programación desactualizados, o métodos de desarrollo obsoletos. Lo más común es encontrar signos de muchas modificaciones y adaptaciones debido a múltiples cambios de mano [11]. Estos sistemas poseen normalmente un alto costo de actualización o reemplazo. Por esto es que surgen técnicas de reingeniería para reducir su complejidad de forma tal que pueda seguir siendo utilizado y adaptado a un costo aceptable [11].

En estos casos, en lugar de tomar un enfoque de mantenimiento y evolución, se suele tomar una actitud de “No arreglar lo que funciona”. El problema con este enfoque es que falla en reconocer las múltiples maneras en la que un sistema puede no “funcionar”. Desde un punto de vista funcional, un software funciona si logra cumplir con la funcionalidad para la cual fue diseñado. Pero desde un punto de vista de mantenimiento o de calidad, un software no funciona si no puede seguir siendo mantenido [11] o si no posee una calidad aceptable (más allá de que funcione).

2.2.4 Soluciones propuestas

Una pregunta importante es cómo se puede controlar la complejidad del software. Una estrategia es la reingeniería de software, en la cual se modifica un sistema existente para mejorar su diseño reduciendo la complejidad. Esta estrategia incluye el uso de la tecnica de

refactorizacion o refactoring [5].

La mantenibilidad, como un atributo de calidad del producto de software, puede ser tratada mediante el uso de patrones de diseño y arquitectura desde las etapas de diseño de software. Esto es, preparar el diseño para soportar la mantenibilidad y evolución a través de la

utilización de patrones previamente estudiados con dicho objetivo en mente. Este “diseño

orientado a soportar el cambio” abarca técnicas de separación de concerns, ocultamiento de

información, uso de mecanismos de herencia y polimorfismo, interfaces, separación del diseño en capas, mantenimiento actualizado de la documentación, y desarrollo orientado a pruebas entre otras.

(14)

diseño, llamados Code Smells. En este tema se destaca el trabajo realizado por Lanza y Marinescu [4], y será estudiado en la Sección 2.3.

Si bien se suele atacar el problema de la calidad de software mediante: metodologías, mejoras de procesos, desde la concepción del diseño, o por medio de métricas, no hay que olvidar que existen otros factores que también pueden indicar futuros problemas. Estos son la falta de documentación actualizada, la falta de pruebas (tests), falta de conocimiento del proyecto o la salida del proyecto del personal que posea conocimientos no documentados, cambios simples que llevan demasiado tiempo de desarrollo o en salir a producción, alta tasa de bugs detectados, etc. [11].

2.3

Code Smell

2.3.1 Definición de

Code Smell

Los Code Smells o Bad Smells son características estructurales de software que pueden

indicar un problema de diseño o de código que hace al software difícil de evolucionar y mantener, y puede desencadenar en refactorizaciones de código [18]. Estos representan síntomas de decisiones de implementación o diseño pobres [5]. El concepto fue introducido

por Fowler [5], quien catalogó 22 tipos de Code Smells, y asoció cada uno con

transformaciones de refactorización correspondientes. Otros autores también han identificado

Code Smells (por ej. Mäntylä [19]), y otros podrían ser identificados en el futuro.

2.3.2 Impacto de los

Code Smells

Se han realizado diversos estudios para investigar la relevancia que los Code Smells tienen

para los desarrolladores [25, 26], la medida en la cual los Code Smells tienden a permanecer

en un sistema de software por largos periodos de tiempo [35,36,37,33], así como también los efectos secundarios de los mismos, como incrementos en la probabilidad de errores [27, 28] o bajas en la mantenibilidad del software [24, 29, 30], en particular debido una menor comprensibilidad [31].

Un estudio importante es el realizado por Palomba [25] sobre la percepción que tienen los

desarrolladores sobre los Code Smells. De este estudio se concluye que no todos los Code

Smells indican realmente problemas a futuro, de acuerdo a la percepción de los

desarrolladores. Esto puede variar de un smell a otro y según la complejidad o gravedad del

mismo. La experiencia y el conocimiento influyen en la identificación de Code Smells por parte

de los desarrolladores [25]. Según este estudio, Long Method es para los desarrolladores uno

de los 2 Code Smells percibidos como amenaza más importantes.

De acuerdo a Fontana [18], la eliminación de un Code Smell tiene que ser siempre evaluada

por una persona que pueda tomar la decisión en base al sistema en el cual este fue encontrado.

2.3.3 Surgimiento de

Code Smells

Los Code Smells son introducidos en los sistemas de software generalmente por

(15)

o porque no se preocupan por diseñar apropiadamente la solución debido a plazos de tiempo de entrega estrictos [25].

De acuerdo a Tufano [22], la mayoría de las veces los artefactos de código son afectados por

Code Smells desde su creación. Sin embargo, también se los suele introducir no solo con la

implementación de nuevas funcionalidades, sino también con la realización de actividades de mantenimiento como operaciones de refactorización, las cuales se llevan a cabo justamente

para evitar estos smells. Esto no es necesariamente un problema de los nuevos

desarrolladores en un proyecto, los desarrolladores más propensos a introducir Code Smells

son aquellos con altas cargas de trabajo y con más presión en la entrega de artefactos.

2.3.4 Identificación de

Code Smells

2.3.4.1 Métricas

Recientemente se han realizado varios estudios sobre la evolución histórica de Code Smells

en sistemas de software [32, 33] así como también sobre el origen de los Code Smells [22].

Estos estudios revelan que los artefactos de software a medida que se vuelven “smelly” como

consecuencia de las actividades de mantenimiento, son caracterizados por tendencias peculiares de sus métricas.

Estas métricas pueden ser utilizadas para evaluar la calidad de software y predecir futuros esfuerzos de desarrollo. Sin embargo no son una herramienta perfecta, dado que hay varios aspectos del diseño de software y su calidad que son difíciles de medir, pero sirven como un punto de partida para un análisis más profundo. Por ejemplo, pueden utilizarse métricas propias de la empresa y de los procesos como la cantidad de bugs detectados en dicha sección de código [4].

Las métricas utilizadas para la identificación y detección de Code Smells son llamadas

métricas de diseño [4].

2.3.4.2 Umbrales

Al trabajar con métricas resulta necesario saber qué se entiende por “demasiado alto”, “demasiada cantidad”, “demasiado pequeño”, entre otros. Estos puntos de referencia

llamados umbrales o “thresholds” sirven como conexión entre el valor de las métricas y una

semántica útil. Un umbral divide el espacio del valor de una métrica en regiones, y dependiendo de en qué región se encuentre un valor particular, se pueden realizar estimaciones sobre la entidad medida basadas en la información tomada [4].

Los umbrales tampoco son perfectos, pero igualmente son útiles en la práctica. Estos se

suelen obtener de análisis estadísticos generalmente aceptados [4]. En general, el valor de

(16)

2.3.4.3 Utilidad de las métricas

Una forma de obtener métricas que provean información real y no solamente números es la utilización del modelo Goal-Question-Metric (GQM) [23], que obliga a definir objetivos antes de realizar mediciones. Primero se definen los objetivos para los cuales se necesitan hallar las métricas, luego por cada objetivo se derivan preguntas que deben ser contestadas para determinar si un objetivo es cumplido o no, y finalmente se define qué métricas deben recolectarse para responder dichas preguntas. Al recolectar las métricas se aplica el mecanismo de filtro o “filtering”, en el cual se comparan los valores de las métricas obtenidos con valores de umbrales para descartar aquella información que no es relevante para el estudio en cuestión [4]. Esta operación devuelve un valor booleano y es utilizada para definir métricas más complejas utilizando el mecanismo de composición (mediante el uso de operadores lógicos AND y OR). El resultado de una composición es también un filtro y se

suele utilizar directamente para afirmar o no la existencia de un Code Smell.

Algunos ejemplos de métricas que son importantes para la comprensión de los Code Smells

tratados en este trabajo son las siguientes:

● Cantidad de líneas de código (LOC): Indica la cantidad de líneas de código que componen un método, clase, función, o cualquier fragmento de código en cuestión. ● Complejidad ciclomática (CYCLO): Indica el número de caminos independientes

dentro de un fragmento de código. Esta métrica mide la complejidad de un método en base a la aplicación de la teoría de grafos [20].

● Máximo nivel de anidamiento (MAXNESTING): Indica el máximo nivel de anidamiento de estructuras de control dentro de un metodo o funcion.

● Cantidad de variables utilizadas (NOAV): Indica la cantidad de variables locales declaradas, parametros, asi como tambien la cantidad de atributos y variables globales utilizadas.

2.3.5

Long Method

y

Brain Method

Un Long Method es un método que es demasiado largo en términos de cantidad de líneas de

código. Este Code Smell es indeseado debido a que afecta la testeabilidad y comprensibilidad

del código, y en consecuencia la mantenibilidad del mismo [4]. Como se mencionó

anteriormente, es uno de los 2 Code Smells críticos para la percepción general de los

desarrolladores. En el Código Fuente 2.1 se observa un ejemplo de un Code Smell de tipo

Long Method. El método en cuestión (makeQualifier) presenta un total de 125 líneas de

código, lo cual hace difícil su comprensión con la simple lectura de las mismas.

static Expression makeQualifier(EOObjEntity entity, Map qualifierMap) {

if (isAggregate(qualifierMap)) {

// the fetch specification has more than one qualifier

int aggregateClass = aggregateExpressionClassForQualifier(qualifierMap); // AND, // OR,

// NOT

if (aggregateClass == Expression.NOT) {

// NOT qualifiers only have one child, keyed with // "qualifier"

Map child = (Map) qualifierMap.get("qualifier");

// build the child expression

Expression childExp = makeQualifier(entity, child);

(17)

// the // result

} else {

// AND, OR qualifiers can have multiple children, keyed with // "qualifiers"

// get the list of children

List children = (List) qualifierMap.get("qualifiers");

if (children != null) {

ArrayList<Expression> childExpressions = new ArrayList<>();

// build an Expression for each child Iterator<Map> it = children.iterator();

while (it.hasNext()) {

Expression childExp = makeQualifier(entity, it.next()); childExpressions.add(childExp);

}

// join the child expressions and return the result

return ExpressionFactory.joinExp(aggregateClass, childExpressions); }

}

} // end if isAggregate(qualifierMap)...

// the query has a single qualifier // get expression selector type

String qualifierClass = (String) qualifierMap.get("class");

// the key or key path we're comparing String key = null;

// the key, keyPath, value, or parameterized value against which // we're

// comparing the key

Object comparisonValue = null;

if ("EOKeyComparisonQualifier".equals(qualifierClass)) { // Comparing two keys or key paths

key = (String) qualifierMap.get("leftValue"); comparisonValue = qualifierMap.get("rightValue");

// FIXME: I think EOKeyComparisonQualifier sytle Expressions are // not

// supported...

return null;

} else if ("EOKeyValueQualifier".equals(qualifierClass)) { // Comparing key with a value or parameterized value key = (String) qualifierMap.get("key");

Object value = qualifierMap.get("value");

if (value instanceof Map) {

Map<String, String> valueMap = (Map<String, String>) value; String objClass = valueMap.get("class"); // can be a

// qualifier class // or java type

if ("EOQualifierVariable".equals(objClass) && valueMap.containsKey("_key")) {

// make a parameterized expression String paramName = valueMap.get("_key");

comparisonValue = new ExpressionParameter(paramName); } else {

Object queryVal = valueMap.get("value");

if ("NSNumber".equals(objClass)) { // comparison to NSNumber -- cast comparisonValue = queryVal; } else if ("EONull".equals(objClass)) {

// comparison to null comparisonValue = null;

} else { // Could there be other types? boolean, date,

// etc.??? // no cast comparisonValue = queryVal; }

(18)

} else if (value instanceof String) { // value expression

comparisonValue = value;

} // end if (value instanceof Map) else...

}

// check whether the key is an object path; if at least one // component is not,

// switch to db path..

Expression keyExp = ExpressionFactory.exp(key);

try {

entity.lastPathComponent(keyExp, Collections.emptyMap()); } catch (ExpressionException e) {

try {

keyExp = entity.translateToDbPath(keyExp); } catch (Exception dbpathEx) {

return null;

} }

try {

Expression exp =

ExpressionFactory.expressionOfType(expressionTypeForQualifier(qualifierMap));

exp.setOperand(0, keyExp);

exp.setOperand(1, comparisonValue);

return exp;

} catch (ExpressionException e) {

return null;

} }

Código Fuente 2.1 Ejemplo de Long Method.

De hecho, en un estudio sobre el efecto de los Code Smells en el esfuerzo de mantenimiento

[29], se concluye que para reducir el esfuerzo de mantenimiento, un enfoque consistente en reducir la cantidad de código y prácticas de trabajo que reduzcan el número de cambios

puede resultar más beneficioso que la refactorización de Code Smells en general.

Además de afectar la comprensibilidad del código, se introduce una carga extra de esfuerzo mental para el lector (desarrollador) de un método al “cambiar de contexto” mentalmente mientras se desarrolla software, al tratar de entender lo que hace dicho método [5].

La detección de Long Methods resulta sencilla. La métrica necesaria es la cantidad de líneas

de código (LOC) la cual puede compararse con valores de umbral estadísticos para crear filtros como se ve por ejemplo en la Fórmula 2.1. Esta expresión al ser verdadera indica que el método es “excesivamente largo”.

𝐿𝑂𝐶 >𝐻𝐼𝐺𝐻(𝐶𝑙𝑎𝑠𝑠) 2

(19)

Si bien medir un Long Method puede ser una tarea sencilla, confiar en una métrica tan simple definitivamente conlleva a resultados equivocados. Por ejemplo, los métodos de inicialización

(initiation methods) suelen ser largos, pero no tiene sentido realizar refactorizaciones dado

que son normalmente fáciles de entender y modificar. Varios autores [19, 4] sugieren la utilización de otras métricas como la complejidad ciclomática y el máximo nivel de anidamiento.

Por ejemplo la composición presentada en la Fórmula 2.2 se asocia a la detección del Code

Smell Brain Method [4].

𝐿𝑂𝐶 > 𝐻𝐼𝐺𝐻(𝐶𝑙𝑎𝑠𝑠)

2 ∧ 𝐶𝑌𝐶𝐿𝑂 ≥ 𝐻𝐼𝐺𝐻 ∧ 𝑀𝐴𝑋𝑁𝐸𝑆𝑇𝐼𝑁𝐺 ≥ 𝑆𝐸𝑉𝐸𝑅𝐴𝐿 ∧ 𝑁𝑂𝐴𝑉 > 𝑀𝐴𝑁𝑌

Fórmula 2.2 Composición de Métricas para definir el Code Smell de Tipo Brain Method.

Los Brain Method son Code Smells más restrictivos que los Long Methods, que no solamente

indica que un método es excesivamente largo, sino que también tiende a centralizar la funcionalidad de la clase. Estos métodos son difíciles de entender, depurar, y prácticamente imposibles de reutilizar [4].

La estrategia de detección propuesta por [4] se basa en la composición de varias métricas definida por [5] y es la siguiente:

● El método es excesivamente largo: Su valor de LOC es mayor que la mitad del valor de umbral estadístico de dicha métrica considerado como alto para una clase.

● El método tiene muchas ramificaciones condicionales (CYCLO): Computadas usando la métrica de complejidad ciclomática de McCabe’s [20]

● El método tiene un nivel de anidamiento profundo: Computada utilizando la métrica de MAXNESTING

● El método utiliza demasiadas variables: Se computa utilizando la métrica NOAV que toma en cuenta variables locales, parámetros, atributos y variables globales. El valor de dicha métrica se considera excesivo si utiliza más variables de las que un humano puede mantener en la memoria de corto plazo.

Los Code Smells que implican aspectos de longitud o duplicación de código suelen

considerarse “Desarmonías de identidad” (Identity disharmony) [4]. Suelen corregirse

eliminando primero la duplicación de código, variables temporales o no utilizadas, y en

particular para los Long Methods o Brain Methods mediante la técnica de refactoring conocida

como “Extract Method”.

2.4

Refactoring

2.4.1 Definición

El proceso de refactorización o refactoring se define como el proceso de cambio de un

(20)

refactorización es un cambio hecho en la estructura interna del software para hacerlo más fácil de entender y de modificar, sin cambiar su comportamiento observable [5].

2.4.2 Aplicación de refactorizaciones

Si bien no se define la refactorización como una etapa del proceso de desarrollo sino más bien como una actividad de mantenimiento, es importante saber cuándo debe realizarse. El proceso de refactorización debería realizarse cuando se agrega funcionalidad, cuando se necesita corregir un bug, cuando se hace revisión de código, o cuando uno se topa varias veces con la necesidad de duplicar el mismo código (también conocida como la Regla del Tres) [5]. Sin embargo Fowler sugiere realizar las refactorizaciones cuando se detecta un

Code Smell, y detenerse al finalizar la eliminación del mismo [5].

Algunas metodologías proponen incluso no realizar ninguna etapa de diseño por adelantado (o realizar en su lugar un diseño pensado vagamente), sino implementar el código con el primer enfoque que se viene a la mente del desarrollador hasta que funcione, y luego utilizar refactorizaciones para darle forma a dicho código. Por ejemplo, estos lineamientos se discuten en la metodología Extreme Programming [6].

El presente trabajo es agnóstico respecto a la metodología o proceso de desarrollo utilizado, y por lo tanto se tratará el tema de refactorizaciones solamente como una herramienta para

solucionar Code Smells particulares que han sido detectados en el código por medio de

métricas, y no como una actividad general de mantenimiento. Fowler en [5] asocia las técnicas

de refactorización con el Code Smell que ayudan a solucionar. En el caso de Long Method,

la refactorización asociada es conocida como Extract Method.

2.4.3

Extract Method

Extract Method es una de las refactorizaciones más comunes, y su mecanismo consiste en

los siguientes pasos:

1. Crear un nuevo método y nombrarlo apropiadamente según la intencionalidad del mismo.

2. Copiar el código a extraer del método original en el nuevo método.

3. Buscar referencias a variables locales utilizadas en el código extraído que fueron declaradas en el método original y definirlas como parámetros del nuevo método. 4. Si se utilizan variables temporales en el código extraído, declararlas en el nuevo

método.

5. Revisar si alguna de estas variables locales fue modificada por el código extraído. Si una variable es modificada, ver si se la puede tratar como una “consulta” y asignar el resultado a la variable en cuestión. Si esto es raro, o si hay más de una variable de este tipo, el método no puede ser extraído de esta forma. En este caso será necesario

usar otras técnicas como “Split Temporary Variable” (Dividir variable temporal) o

Replace Temp with Query” (Reemplazar variable temporal con consulta).

6. Invocar al nuevo método pasando como argumento las variables locales requeridas, reemplazando el código extraído en el método original por dicho llamado.

(21)

2.4.4 Problemas de la refactorización de código

Si bien Fowler afirma que las refactorizaciones ayudan a mejorar la comprensibilidad del código, encontrar bugs, y mejorar la performance del programa entre otras ventajas [5], en la práctica se presentan tendencias no consistentes, y se encuentran casos donde la refactorización manual mejora un atributo de calidad en algunas clases pero debilita el mismo atributo de calidad en otras clases del mismo sistema. Por lo tanto, no se podría confirmar la generalización de que el proceso de refactorización es siempre una práctica que mejora la calidad del software [21]. Otro ejemplo es el estudio realizado por Tufano en el cual se encontraron casi 400 casos en los cuales las operaciones de refactorización manual

introdujeron nuevos smells (entre el 4% y 11% en promedio de las refactorizaciones

realizados) [22].

Aun así, se puede utilizar la refactorización de forma manual como un proceso para ayudar a entender y validar el entendimiento actual sobre cómo funciona un sistema de software [5, 11].

Otros escenarios donde no resulta conveniente realizar refactorizaciones es cuando intervienen bases de datos o cuando la refactorización implica modificar interfaces. En el primer caso, la mayoría de las aplicaciones de negocio están ligadas a los datos y su estructura que le dan soporte. Además del riesgo de introducir errores, las migraciones de los cambios resultan difíciles de realizar. En el segundo caso, se puede perder la noción de donde se utiliza el método o clase que está siendo modificado y por lo tanto introducir errores [5].

2.4.5 Minimización de riesgos

Una forma de minimizar el riesgo de introducir defectos consiste en utilizar un proceso basado en tests automatizados, que puedan ser repetidos y almacenados. Estos tests, si son bien diseñados, deberían exhibir las siguientes propiedades:

- poder ejecutarse de forma automática;

- poder ser almacenados (persistentes);

- ser repetibles;

- poder asociarse a componentes de software individual (unit testing);

- y ser independientes entre sí [11].

(22)

Capítulo 3: Estado del arte

En este capítulo se realiza un repaso sobre las herramientas de software existentes en la

actualidad para la eliminación del Code Smell de tipo Long Method y Brain Method. Antes de

comenzar dicho repaso, en la sección 3.1 se realiza una explicación del proceso de refactorización automática para ayudar a comprender mejor el grado de automatización de cada una de las herramientas presentadas luego en la sección 3.2.

3.1 Automatización del

refactoring

mediante herramientas de

software

Como se mencionó en el Capítulo 2, varios de los problemas asociados a la refactorización de código se deben a la ejecución manual de este proceso. Además de consumir demasiado tiempo, la refactorización manual es propensa a errores y se vuelve difícil de manejar en grandes cantidades de código. Para esto se sugiere la utilización de herramientas automáticas, las cuales deben ser consideradas cuidadosamente dado que no todas presentan el mismo grado de automatización, o automatizan distintas partes del proceso de refactorización.

3.1.1 Descomposición del proceso de

refactoring

Antes de analizar las distintas herramientas disponibles para la refactorización es importante pensar el proceso de refactorización en etapas para poder analizar el ámbito de aplicación de cada herramienta y entender mejor las ventajas y desventajas de cada una. Tourwe [38] identifica 3 etapas asociadas al proceso de refactorización:

1) Identificación de cuándo una aplicación debe ser refactorizada.

2) Propuesta de refactorizaciones que podrían ser aplicadas y en qué lugar. 3) Aplicación de la refactorización seleccionada.

La última etapa suele dividirse en dos fases: chequear si las pre-condiciones apropiadas de la refactorización se cumplen para asegurar que la refactorización es válida (el código seguirá siendo compilable) y se preserva el comportamiento, y aplicar los cambios necesarios (debido a la refactorización).

Como se puede ver, el proceso de refactorización automatizado no es simplemente una forma de realizar extracciones seguras o válidas, sino que también incluye la identificación de problemas y la posibilidad de elegir (de manera automática, o bien por medio de entrada del usuario) entre varias alternativas de refactorización para solucionarlos. La etapa de identificación de problemas suele implementarse mediante los mecanismos de identificación

de Code Smells.

3.2 Trabajos relacionados

3.2.1 Automed

(23)

de refactorizar los Long Method al facilitar la identificación de extracciones en el código. La identificación consiste en identificar qué sentencias de código de un método extraer sin que esto modifique la funcionalidad y no cambie el flujo de ejecución. Identificarlos es una tarea tediosa para el desarrollador ya que tiene que comprobar que no se modifique el comportamiento del código manualmente. Al facilitar la identificación, se mejora el proceso de refactorización en un 40% (aproximadamente) con respecto al costo del tiempo que lleva

ejecutar todo el proceso de refactoring.

Para la identificación de los fragmentos de código a extraer se aplican técnicas que recorren

el código de manera recursiva en búsqueda de statements que estén separados en bloques

con comentarios, o simplemente separados por espacios en blanco. La justificación de realizar esto es que los desarrolladores programan de una manera que dejan porciones de código separados del resto porque tienen una funcionalidad diferente o son más complejos.

Además, en la identificación, también se incluyen statements del tipo If, For, While o cualquier

otro tipo de statement que podrían ser extraídos.

Luego, estos statements se priorizan según un criterio de costos y beneficios. El costo

considerado en el enfoque es el acoplamiento entre el nuevo método creado y el original. Este se calcula a partir de la cantidad de parámetros que tiene el método extraído sumado a la cantidad de variables que retorna el método. En el contexto de proyectos de Java, las variables de retorno pueden ser una o ninguna, porque el lenguaje no permite que se retornen varias variables en un método. El beneficio se considera como la cantidad de líneas que se extraen en el nuevo método. Este se expresa en la Fórmula 3.1.

𝑅(𝑓) =

𝐵𝑒𝑛𝑒𝑓𝑖𝑐𝑖𝑜(𝑓)

𝐴𝑐𝑜𝑝𝑙𝑎𝑚𝑖𝑒𝑛𝑡𝑜(𝑓)

=

|𝑓|

(|𝑃𝑒𝑛𝑡𝑟𝑎𝑑𝑎(𝑓)| + |𝑃𝑠𝑎𝑙𝑖𝑑𝑎(𝑓)|)

Fórmula 3.1 Cálculo del beneficio de una extracción.

3.2.2 PMD

PMD es un proyecto de software libre diseñado para inspeccionar código Java y resaltar estructuras ineficientes, como: variables locales no utilizadas, sentencias de importación duplicadas, o bloques try/catch vacíos. PMD le da a los programadores un enfoque preventivo para limpiar su código. PMD inspecciona código Java utilizando un enfoque basado en reglas. Estas reglas pueden ser definidas tanto por XPath (una sintaxis basada en XML) o clases Java. PMD incluye una serie de reglas consideradas comunes en toda aplicación Java, todas incluidas en su distribución principal. Estas están organizadas en conjuntos de reglas (rulesets) dependiendo de su funcionalidad [40].

PMD chequea el código fuente de acuerdo a reglas y produce un reporte, de la siguiente manera [41]:

● Se pasa un nombre de archivo y nombre de conjunto de reglas a PMD ● PMD entrega un InputStream del archivo al parser de JavaCC generado ● PMD obtiene del parser una referencia a un árbol sintáctico abstracto (AST)

● PMD entrega este AST a la capa de tabla de símbolos que construye los alcances, busca las declaraciones, y busca los usos de estos símbolos.

(24)

● Cada regla en el conjunto de reglas atraviesa el AST y comprueba la existencia de problemas. Estas reglas pueden también explorar la tabla de símbolos y nodos del DFA

● El reporte es completado con las violaciones a las reglas, y es generado como un documento XML, o HTML entre otros

En particular, para soportar la detección de Long Methods PMD incluye la regla

ExcessiveMethodLength.

3.2.3 Checkstyle

Checkstyle es una herramienta de desarrollo para ayudar a los programadores a escribir código Java que adhiere a estándares de código. Esta herramienta automatiza el proceso de chequear código Java para liberar a las personas de esta importante tarea que resulta tediosa. Esto la hace una herramienta ideal para proyectos que buscan cumplir un estándar de código [42].

Checkstyle puede chequear muchos aspectos del código fuente. Puede encontrar problemas de diseño de clases y problemas de diseño de métodos. Cuenta también con la habilidad de chequear problemas relacionados al formato y estructura visual del código.

Dentro de los chequeos provistos por la herramienta se encuentra el llamado MethodLength, el cual comprueba que el número de líneas de un método no exceda un determinado valor. Este chequeo puede ignorar líneas en blanco y comentarios, pero no cuenta la cantidad de sentencias reales. Sin embargo, existe un chequeo para verificar que solo exista una sentencia por línea de código.

Esta herramienta se encuentra disponible como una aplicación de línea de comandos, como

una tarea para la biblioteca Ant, y también como plugin para diferentes entornos de desarrollo

o herramientas de compilación.

3.2.4 Herramientas integradas en entornos de desarrollo

La mayoría de los entornos de desarrollo actuales cuentan con soporte para la realización de refactorizaciones. Por ejemplo el IDE Eclipse cuenta con una herramienta que asiste en la

aplicación de refactorizaciones Extract Method (entre otros refactorings). Esta cuenta con una

interfaz para interactuar con el usuario y realiza el chequeo de pre y post-condiciones correspondientes para asegurar que la extracción es válida. Además puede ser utilizada por

plugins de Eclipse por medio de la API LTK (Language Toolkit)

Otro entorno de desarrollo de proyectos Java muy popular es Intellij IDEA, el cual además de contar con un asistente de extracción de métodos, provee herramientas de inspección de

código incluyendo la detección de Long Methods.

(25)

estos ofrecen una vista local del código, orientada a archivos, lo cual dificulta la navegación de una jerarquía de clases entera y las diferentes implementaciones de un método a lo largo de esta. Como consecuencia, dichos entornos son incapaces de presentar una vista global de la estructura general y diseño de la aplicación.

3.2.5 JSpIRIT y Bandago

3.2.5.1 JSpIRIT

JSpIRIT (Java Smart Identification of Refactoring opportunITies) [43] es un plugin para el

entorno de desarrollo Eclipse que asiste a los desarrolladores en la identificación y

priorización de Code Smells. Este soporta la identificación de 10 Code Smells siguiendo las

estrategias de detección presentadas por Lanza y Marinescu [4]: ■ Brain Class

Brain Method

■ Data Class

■ Disperse Coupling ■ Feature Envy ■ God Class

■ Intensive Coupling ■ Refused Parent Bequest ■ Shotgun Surgery

■ Tradition Breaker

Para priorizar los Code Smells, JSpIRIT provee varios tipos de clasificaciones que usan

diferentes criterios como: la relevancia del tipo de Code Smell, o escenarios de modificabilidad

entre otros. JSpIRIT soporta además la identificación de 2 tipos de aglomeraciones de Code

Smells. Las aglomeraciones son grupos de Code Smells interrelacionados (por ejemplo, Code

Smells relacionados sintácticamente dentro de un componente) que probablemente indican

juntos la presencia de un problema arquitectural [43].

3.2.5.2 Bandago

Bandago [10] es una herramienta implementada sobre JSpIRIT cuyo enfoque consiste en

remover Brain Methods por medio de la extracción de fragmentos de código (refactorizaciones

de tipo Extract Method) mientras se consideran aspectos de legibilidad y extensibilidad del

código resultante. Bandago asiste a los desarrolladores con técnicas de búsqueda automática

para corregir las instancias de Brain Methods. Esto se debe a que seleccionar los fragmentos

de código a extraer puede ser desafiante por diversas razones [44]. Algunos ejemplos son: es difícil identificar sentencias relacionadas cohesivamente, puede haber dependencias entre los fragmentos seleccionados y el resto del código, el comportamiento del método debe ser preservado, entre otros.

Bandago sigue un ciclo de trabajo iterativo, que toma como entrada el código fuente de un

método identificado como Brain Method por JSpIRIT y produce como salida soluciones

alternativas para el smell. Estos Brain Methods son identificados por medio de una estrategia

(26)

Internamente Bandago realiza una búsqueda heurística usando un algoritmo de tipo

Simulated Annealing [45]. El enfoque involucra las siguientes 4 actividades:

1. Obtener las sentencias de un método: Dada una instancia de Brain Method, esta

actividad provee un conjunto de sentencias que pueden ser extraídas del mismo. Las

sentencias son filtradas y agrupadas de acuerdo a su tipo (Ejemplo: Sentencias “if”,

sentencias “while”, etc.)

2. Obtener una sentencia candidata: Las sentencias son evaluadas basándose en diferentes criterios, llamados operadores, como por ejemplo la longitud o complejidad de la misma.

3. Extraer la sentencia candidata: La sentencia elegida es extraída en un nuevo método

vía una refactorización Extract Method. Esta refactorización es aplicado en un modelo

del programa almacenado en memoria, lo que se conoce como refactorización virtual [46,47]. Una vez que la solución es generada y evaluada, los cambios introducidos en memoria son descartados. Una refactorización es aplicada directamente en el código fuente cuando el desarrollador escoge una solución en concreto de entre todas las propuestas por Bandago.

4. Chequear la existencia de Brain Methods: Después de aplicar una refactorización,

Bandago chequea que ambos métodos (el método afectado y el nuevo método

creado) han dejado de ser Brain Methods. En caso contrario se exploran

refactorizaciones adicionales

5. Evaluar solución general: Cuando una solución para un Brain Method es encontrada,

un conjunto de métricas son computadas con el fin de evaluar su “goodness”. Estas

métricas ayudan al desarrollador a elegir una “buena” solución de entre las propuestas.

Luego de realizar la actividad 5, se itera un número predefinido de veces, desde la actividad 1 hasta la actividad 5, con el fin de devolver un número predeterminado de soluciones al

desarrollador. Por el momento Bandago solo soporta el Code Smell de tipo Brain Method.

3.2.5.2.1 Simulated Annealing

Simulated Annealing es un algoritmo de búsqueda metaheurístico basado en una analogía

con los procesos de calentamiento y enfriamiento de materiales en metalurgia [48]. Ha sido usado para resolver diferentes problemas de optimización con múltiple óptimos locales [49, 48], y en particular, ha sido utilizado para buscar refactorizaciones [50, 51]. En este contexto un óptimo local es la mejor solución encontrada por el algoritmo, pero no es necesariamente la mejor solución al problema, la cual es llamada óptimo global.

Simulated Annealing sigue un método iterativo que empieza por considerar un estado I. En

(27)

Bandago utiliza Simulated Annealing para buscar opciones de refactorización de Brain

Methods dado que este algoritmo permite explorar un amplio rango de diferentes soluciones

para corregir el Brain Method, usando solamente una transformación. Incluso el algoritmo

puede aceptar soluciones parcialmente satisfactorias, con el objetivo de encontrar soluciones más adelante por medio de una pequeña secuencia de transformaciones.

En el Código fuente 3.1 se observa el pseudocódigo utilizado por Bandago, el cual es una

adaptación del algoritmo Simulated Annealing considerando Brain Methods y

refactorizaciones Extract Method.

1. generateSolutions(BrainMehod bm){

2. while(solutions.size()<numberOfSolutions){ 3. if(satisficing(simulatedAnnealing(bm)) 4. solutions.add(simulatedAnnealing(bm)); 5. }

6. //activity5

7. measureSolutions(solutions); 8. return(solutions);

9. }

10. simulatedAnnealing(BrainMethod bm){ 11. state=bm.getCode();

12. temperature=initialTemperature; 13. actualIteration=0;

14. while(actualIteration<maxIterations){ 15. //activity1

16. statementSet=state.getStatements(); 17. //activity2

18. candidateStatement=statementSet.getPriorizedStatement(); 19. //activity3

20. newState=state.applyExtractMethod(candidateStatement); 21. //activity4

22. if(!stillBrainMethods(newState))//evaluation function 23. return newState;//satisfying solution

24. elseif(random()<tempFunct(state,newState,temperature,actualIteration)) 25. //cooling schedule

26. state=newState;

27. temperature=coolDownFactor∗temperature; 28. actualIteration++;

29. }

30. return(state); 31. }

Código Fuente 3.1 Pseudocódigo para el algoritmo de Simulated Annealing implementado en Bandago.

Notar que la función de “fitness” de este algoritmo no busca soluciones óptimas sino

soluciones satisfactorias (es decir, soluciones válidas con respecto a Brain Method).

En la Figura 3.1 se pueden observar los Code Smells detectados por la herramienta JSpIRIT

(28)

Figura 3.1 Captura de pantalla de la herramienta JSpirit.

Luego de seleccionar un Code Smell a eliminar por Bandago, se observa una lista como la

que se puede apreciar en la Figura 3.2, con las distintas soluciones generadas por la herramienta. Cualquiera de estas soluciones puede ser aplicada por el desarrollador.

Figura 3.2 Captura de pantalla de la herramienta Bandago.

3.2.6 JDeodorant

JDeodorant [9] es una herramienta que permite identificar oportunidades de refactorización

para 4 tipos de Code Smells en sistemas Java, a saber: Feature Envy, State Checking, God

(29)

El enfoque de la herramienta para la remoción del Code Smell Long Method es orientado a refactorizaciones. Esto quiere decir que la herramienta no se enfoca en la detección y

corrección del Code Smells, sino simplemente en la aplicación de refactorizaciones Extract

Method donde sea posible, respetando un mínimo número de líneas a extraer por

refactorización. Este enfoque presenta tres ventajas sobre los enfoques existentes [9]: ● Provee una solución completa al problema de mejorar la calidad de diseño tomando

como ventaja el hecho de que los Bad Smells son mapeados directamente a

soluciones de refactorización específicas

● Tiene la habilidad de producir soluciones factibles y que preservan el comportamiento, mediante la observación de un conjunto de precondiciones que deben cumplirse ● Tiene la habilidad de pre-evaluar el efecto de las oportunidades de refactorización

identificadas en calidad de diseño, y así proveer un ranking de soluciones de

refactoring permitiendo la priorización de los efectos de mantenimientos en las partes

de un programa que más lo necesitan

El enfoque tomado para la búsqueda de oportunidades de refactoring Extract Method se basa

en el concepto de Program Slicing. Algo para destacar de este enfoque es que permite

realizar extracciones de código no consecutivos, lo que lleva a aumentar la cantidad de oportunidades de refactorización encontradas. Esto lleva a una complejidad más alta cuando

se tienen que realizar los chequeos de Program Slicing [52], ya que hay que controlar todas

las ramas de ejecución. Mediante los chequeos nombrados, JDeodorant mantiene la principal premisa de las refactorizaciones, que es que no se modifique el comportamiento del código una vez aplicada la refactorización.

Al seguir un enfoque orientado a refactorizaciones, la herramienta no sigue fielmente el modelo de proceso de refactorización propuesto por Tourwe [38]. En el caso particular de la

búsqueda de oportunidades de refactorización para Extract Method, estas oportunidades son

buscadas independientemente de si el método en análisis presenta o no un Code Smell de

tipo Long Method. La herramienta simplemente intenta encontrar oportunidades de

refactorización donde se extraiga una cantidad de sentencias mayor a un umbral predefinido

por el usuario. Como es de esperarse de esta falta de identificación de smells, tampoco se

revisará luego de aplicarse una refactorización si el Code Smell fue eliminado o no. Esta etapa

de búsqueda de oportunidades se realiza sin intervención del usuario, y produce como salida un conjunto de posibles grupos de sentencias a extraer, diferenciadas por la variable utilizada como criterio.

La herramienta JDeodorant se encuentra implementada como un plugin de Eclipse.

3.2.6.1 Program Slicing

El enfoque tomado para la búsqueda de oportunidades de refactoring Extract Method se basa

en el concepto de Program Slicing. Este enfoque maneja algoritmos de dos categorías

principales. El primer algoritmo identifica oportunidades de refactorización donde la

computación completa de una variable local o parámetro (complete computation slice) puede

ser extraída, significando que todos los slices resultantes contendrán todas las sentencias de

asignación modificando el valor de la variable local. El segundo algoritmo identifica oportunidades de refactorización donde todas las sentencias afectando el estado de un objeto

Referencias

Documento similar

Después de una descripción muy rápida de la optimización así como los problemas en los sistemas de fabricación, se presenta la integración de dos herramientas existentes

diabetes, chronic respiratory disease and cancer) targeted in the Global Action Plan on NCDs as well as other noncommunicable conditions of particular concern in the European

En cada antecedente debe considerarse como mínimo: Autor, Nombre de la Investigación, año de la investigación, objetivo, metodología de la investigación,

En cuarto lugar, se establecen unos medios para la actuación de re- fuerzo de la Cohesión (conducción y coordinación de las políticas eco- nómicas nacionales, políticas y acciones

La campaña ha consistido en la revisión del etiquetado e instrucciones de uso de todos los ter- mómetros digitales comunicados, así como de la documentación técnica adicional de

[r]

Luis Miguel Utrera Navarrete ha presentado la relación de Bienes y Actividades siguientes para la legislatura de 2015-2019, según constan inscritos en el

Fuente de emisión secundaria que afecta a la estación: Combustión en sector residencial y comercial Distancia a la primera vía de tráfico: 3 metros (15 m de ancho)..