Universidad Nacional del Centro
de la Provincia de Buenos Aires
Trabajo Final
Análisis y mejoras de usabilidad y performance
sobre una herramienta de asistencia en el
refactoring de aplicaciones
Alumnos:
Iván Masson Roman Pastore
Director: Codirector:
Dr. Santiago Vidal Dr. Andrés Díaz Pace
Índice General
1. Introducción 8
1.1. Motivación ……….. 9
1.2. Estado del arte ………... 11
1.3. Objetivos ………. 11
1.4. Organización del documento ……….. 13
2. Evolución de los sistemas 14 2.1 Mantenimiento de los sistemas ……… 14
2.2 Code Smells ………. 17
2.2.1 Tipos de Code Smells ………. 19
2.3 Aglomeraciones ……… 20
2.3.1 Tipos de aglomeraciones ………. 23
2.4 JSpIRIT ………. 26
3. Trabajos relacionados 29 3.1 Factores de éxito ……….. 29
3.2 Herramientas ……….... 34
3.3 Técnicas de visualización ……….... 38
4. Optimización: Performance 45 4.1. Análisis del flujo de ejecución actual ………. 46
4.2 Determinar el cuello de botella ……….... 49
4.3 Solución propuesta ………... 52
5. Visualización 62 5.1 Visualización de resultados actual ………... 62
5.2 Ventajas de la utilización de visualizaciones gráficas ………. 64
5.3 Solución propuesta para la visualización de code smells ……….... 67
5.4 Solución propuesta para la visualización de aglomeraciones …….. 74
6. Caso de estudio #1: Optimización 83
6.1 Hipótesis y operatoria ……….. 83
6.1.1 Hipótesis ………. 84
6.1.2 Operatoria ………... 85
6.2 Análisis e interpretación ………... 86
6.3 Resultados ……….... 89
6.4 Riesgos de la validez de los resultados ……… 89
7. Caso de estudio #2: Visualización Aplicación: Health Watcher 91 7.1 JSpIRIT Análisis de code smells ………... 91
7.1.1 Vista de Heat Map ……….. 91
7.1.2 Vista tradicional ……… 99
7.2 JSpIRIT Análisis de aglomeraciones ………... 101
7.2.1 Vistas desarrolladas para aglomeraciones ………. 102
7.3 Resultados ………... 109
7.4 Riesgos de la validez de los resultados ………... 110
8. Caso de estudio #3: Visualización Aplicación: Mobile Media 112
8.1 JSpIRIT Análisis de code smells ……….. 112
8.1.1 Vista de Heat Map ………... 112
8.1.2 Vistas desarrolladas para aglomeraciones ………... 120
8.2 Resultados ……….. 125
8.3 Riesgos de la validez de los resultados ……….. 126
9. Conclusión 128
9.1 Conclusiones finales ………... 128
9.2 Resultados ……….……….. 129
9.3 Limitaciones ……….……….. 130
9.4 Trabajos futuros ……….………. 131
10. Bibliografía 134
Índice de Figuras
2.1 Metamodelo para las aglomeraciones . . . 22
2.2 Aglomeración Intraboundary . . . .23
2.3 Aglomeración Crossboundary . . . .24
2.4 Aglomeración tipo Hierarchical . . . 25
2.5 Interfaz gráfica de JSpIRIT . . . .28
3.1 Mala práctica encontrada por el plugin Findbugs para Eclipse . . . 32
3.2 Técnicas de visualización de D’Ambros y Lanza . . . .38
3.3 Matriz de evolución . . . .39
3.4 Vista de entorno de la herramienta Stench Blossom . . . .41
3.5 Vista de mapa de paquetes de la herramienta inCode . . . .42
3.6 Health screen construido con simbología básica (Figura 3.7) . . . 44
3.7 Simbología o bloques básicos de la visualización propuesta en [17] . . . . .44
4.1 Representación gráfica del AST en la IDE Eclipse . . . 46
4.2 Fragmento de código que parsea el código fuente de un proyecto . . . .47
4.3 Diagrama de secuencia: llamados a métodos en un flujo de ejecución normal de JSpIRIT . . . 48
4.4 jProfiler: Demora (en milisegundos) del árbol de métodos llamados sobre una ejecución de JSpIRIT . . . .50
4.5 jProfiler: Cantidad de memoria (Kilobytes) consumida por cada método llamado sobre una ejecución de JSpIRIT . . . .51
4.6 Atender eventos de cambio de recursos en el workspace . . . .53
4.7 Solución para almacenar las clases modificadas previo a cada análisis . . .54
4.8 Clases A y B, donde A es un caso general de B . . . .55
4.9 Las clases A y B pertenecen al mismo paquete . . . 56
4.10 Las clases A y B pertenecen diferentes paquetes . . . .56
4.11 Grafo que representa dependencia entre clases . . . 57
4.12 Las clases A y B pertenecen diferentes paquetes, B importa a A . . . 57
4.13 Solución para la construcción del grafo de dependencias entre clases . . 59
4.14 Solución propuesta para mantener la consistencia del listado de smells en las sucesivas ejecuciones . . . 60
5.1 Vista de code smells . . . .63
5.2 Vista de aglomeraciones . . . .63
5.3 los primeros 10 code smells en el ranking del análisis de MobilePhone . . 66
5.4 Vista propuesta para la visualización de code smells . . . .69
5.5 “healthwatcher.model.employee” es el paquete más afectado . . . .71
5.6 Paquete con mayor cantidad de smells totales . . . 72
5.7 Paquete con mayor cantidad de smells totales . . . 73
5.8 Dependencias del paquete “healthwatcher.view.command” . . . 73
5.9 Vista de aglomeraciones IntraComponent y Hierarchical . . . .75
5.10 Con un contorno rojo se resaltan los smells miembros de una aglomeración del tipo Hierarchical . . . 76
5.11 Dependencias del paquete “ubc.midp.mobilephoto.core.ui.datamodel” . .77 5.12 Zoom sobre el paquete “ubc.midp.mobilephoto.core.ui.datamodel” . . . . 79
5.13 Navegación hacia el código del método afectado . . . .80
5.14 Vista de aglomeraciones IntraClass . . . .81
5.15 El paquete “ubc.midp.mobilephoto.core.ui.controller” luego de haber ocultado sus elementos . . . 82
6.1 Captura: medición de tiempos de utilización de CPU con JProfiler . . . .84
6.2 Captura: medición de consumo de memoria con JProfiler . . . .85
6.3 Tiempos de las ejecuciones de Health Watcher . . . 88
6.4 Tiempos de las ejecuciones de Mobile Media . . . 88
7.1a Health Watcher Vista de Heat Map . . . 93
7.1b Health Watcher Vista de Heat Map: detalle de smells . . . .93
7.2 Arquitectura Cliente/Servidor de tres capas . . . .96
7.3 Vista de módulos de Health Watcher . . . .96
7.4 Violación en la arquitectura de Health Watcher . . . 97
7.5 Violación en la arquitectura de Health Watcher . . . 99
7.6 Top 10 smells rankeados por JSpIRIT . . . 100
7.7 Vista de aglomeraciones IntraComponent/Hierarchical . . . .103
7.8 Paquete healthwatcher.data.rdb . . . 104
7.9 Intensive Coupling en métodos de la clase ComplaintRepositoryRDB . .106 7.10 Vista de aglomeraciones IntraClass . . . 107
7.12 Aglomeraciones del paquete healthwatcher.model.complaint . . . 109
8.1 Vista Heat Map de Mobile Media . . . .113
8.2 Método PhotoViewController.handleCommand . . . .116
8.3 Dependencias del paquete ubc.midp.mobilephoto.core.ui.controller . . . . 117
8.4 Fragmento extraído del método MediaController.handleCommand() . . . 118
8.5 Violaciones en la arquitectura de Mobile Media . . . .119
8.6 Métodos afectados por Shotgun Surgery . . . .120
8.7 Vista de aglomeraciones IntraComponent . . . .121
8.8 Vista de aglomeraciones IntraClass . . . .122
8.9 Problema que se extiende a más de un paquete . . . .123
8.10 Aglomeración de smells Feature Envy en el paquete ubc.midp.mobilephoto.core.ui.datamodel . . . .124
8.11 Aglomeración jerárquica sobre subclases de AlbumData . . . .125
Índice de Tablas
2.1 Code Smells catalogados por Lanza y Marinescu . . . 19
3.1 Reglas generales para las herramientas de detección de smells . . . 30 3.2 Herramientas de detección de code smells . . . 34 3.3 Soporte para code smells . . . .37
5.1 Convenciones de las tooltips para cada elemento de la vista . . . 78
6.1 Características de la computadora utilizada para el experimento . . . .83 6.2 Resultados de las mediciones de tiempos y consumo de memoria . . . .86
7.1 Code smells afectando al paquete healthwatcher.view.command . . . 94 7.2 Criterio de relevancia utilizado para el ranking de smells . . . 100 7.3 Aglomeraciones del paquete healthwatcher.data.rdb . . . 104
8.1 Smells del paquete ubc.midp.mobilephoto.core.ui.controller . . . .114
Capítulo 1
Introducción
La evolución y mantenimiento de los sistemas de software es una tarea costosa en el proceso de desarrollo de software. Dichos costos tienden a crecer exponencialmente en la medida en que los sistemas se vuelven más grandes y complejos [1]. Una de las mayores preocupaciones en la evolución y mantenimiento de sistemas es la existencia de problemas estructurales de diseño que no son descubiertos y por ende no son subsanados en las etapas tempranas del desarrollo. Este tipo de problemas en el diseño pueden ser identificados mediante “code smells” [2]. Un code smell es un síntoma en el código fuente del sistema que ayuda a identificar un problema de diseño. Los smells permiten a los desarrolladores detectar fragmentos de código que deberían ser reestructurados, para de esa manera mejorar la calidad del sistema. En este aspecto los smells actúan como antipatrones de diseño que ponen en evidencia estas debilidades del sistema.
Existen diferentes herramientas semiautomatizadas para la identificación de smells en un sistema tales como inCode o iPlasma , sin embargo una de las 1 2 mayores limitaciones de dichas herramientas es que usualmente encuentran una gran cantidad de smells; esto puede volverse un problema para el desarrollador por diversas razones. En principio el desarrollador puede verse abrumado por la cantidad de información a analizar, teniendo en cuenta que las herramientas solo señalan los smells y el desarrollador es finalmente quien decide en cada caso si se trata de un problema o no y de sí efectivamente merece que se le otorgue tiempo de desarrollo a solucionar el smell. Una técnica que suele utilizarse para solucionar los code smells es el refactoring [3]. El refactoring es una técnica controlada para mejorar el diseño del código de un sistema. Consiste en aplicar una serie de transformaciones al código para mejorar ciertos aspectos estructurales del mismo sin alterar su comportamiento.
1 http://www.intooitus.com/products/incode
Por otro lado, el desarrollador es quien debe decidir qué problemas son relevantes para la “salud” del sistema y cuales interfieren con el objetivo para el que fue diseñado. Bajo estas condiciones es válido mencionar que en la práctica no todos los smells encontrados en un sistema indican un problema de diseño, por ej. pueden existir métodos extensos en ciertas clases, pero cuya extensión puede estar relacionada a la complejidad de la tarea que realizan y no a la falta de modularización. En este sentido, cada smell encontrado requiere de un análisis particular, lo que obliga al desarrollador a detenerse en cada smell de la lista resultante de un análisis y seleccionar aquellos que, bajo su criterio respaldado por su entendimiento del sistema, necesitan ser reparados.
En este contexto es necesaria una herramienta que asista al desarrollador en la tarea de identificar aquellos smells que sean prioritarios para la evolución y mantenimiento del sistema.
En esta línea se ha propuesto SpIRIT (Smart Identification of Refactoring opportunITies) [4] una herramienta semiautomatizada que realiza una priorización de los code smells de un sistema de acuerdo a su criticidad. Se definen como problemas críticos aquellos que comprometen la arquitectura de un sistema. Luego los mismos autores proponen JSpIRIT (Java Smart Identification of Refactoring opportunITies) que se trata de un plugin para el IDE de desarrollo Eclipse y el cual se utiliza en el desarrollo de éste trabajo.
1.1 Motivación
Si bien JSpIRIT permite un ranking eficaz de los code smells, posee algunas falencias en cuanto a usabilidad y performance. Los tiempos de respuesta altos son una característica indeseable que presenta la herramienta. Para el análisis realizado por JSpIRIT es necesario escanear todo el código fuente del sistema a analizar, esto conlleva a un alto tiempo de respuesta en sistemas con gran cantidad de componentes, lo que se vuelve un problema que impacta en la performance y usabilidad de la herramienta.
En este trabajo se apunta a proveer a JSpIRIT de mejoras que tendrán un impacto positivo en su usabilidad, aspecto relevante debido a que el objetivo de JSpIRIT es pertenecer a la “toolbox” de un desarrollador, para esto debe satisfacer las características que demanda un entorno de desarrollo integrado o IDE [5], como un bajo consumo de recursos y tiempos de respuesta aceptables, de lo contrario se perderá el interés del desarrollador por su utilización.
Como se mencionó anteriormente, otro aspecto importante de una herramienta de estas características es la representación de los resultados obtenidos. Una representación clara de los resultados facilita la comprensión de los mismos ahorrando tiempo al desarrollador, y a su vez otorga la posibilidad de proveer información extra sobre el análisis como, por ejemplo, en qué lugares del sistema se concentra la mayor cantidad de smells o si existe alguna relación entre ellos. También es posible que el desarrollador decida solucionar solo un cierto tipo de problemas con lo cual sería de utilidad que sepa las zonas donde estos se encuentran. Incluso, la sola visualización de los smells en el contexto estructural del sistema provee al desarrollador de una perspectiva más clara de la situación del sistema y le permite aislar y evaluar más precisamente los diferentes sectores. Este trabajo pretende reforzar este aspecto con el agregado de visualizaciones de resultados obtenidos en JSpIRIT para enriquecer el análisis y asistir al desarrollador en la identificación de los componentes del sistema que requieren de refactoring.
1.2 Estado del arte
Pocos trabajos proponen la utilización de medios gráficos para el análisis de code smells [12, 9, 16, 17]. Y son aún menos los que contemplan a las relaciones que pueden existir entre smells [7] como un factor determinante en la detección de problemas de diseño en los sistemas.
Finalmente, algunos trabajos lidian con la refactorización de code smells [12, 13, 14, 15]. Sin embargo, usualmente están enfocados en soluciones parciales (como la extracción de un método) que no son aplicables a diferentes tipos de smells o solo proveen sugerencias para la refactorización de los mismos.
1.3 Objetivos
El primer objetivo de este trabajo es mejorar ciertos aspectos del plugin JSpIRIT que impacten en la usabilidad del mismo. Específicamente, se busca reducir los tiempos de respuesta de JSpIRIT para la identificación y priorización de smells. Para lograr esto se buscó identificar y reducir los “cuellos de botella” en la ejecución del plugin bajo condiciones normales. En otras palabras, analizar el flujo de ejecución de la herramienta y modificarlo con el objetivo de reducir el consumo de recursos computacionales (tiempo de cpu y memoria).
Se sometió al plugin a ejecuciones en proyectos de distinta envergadura con la asistencia de una herramienta de profiling [6] para identificar los sectores de mayor consumo de recursos en el flujo de ejecución. En base a los resultados obtenidos se aplicaron técnicas de programación y algorítmicas para reducir los costos computacionales y mejorar la performance general del plugin.
Por otro lado, el segundo objetivo del trabajo consiste en proveer nueva funcionalidad para facilitar el análisis de los resultados obtenidos de las ejecuciones mediante representaciones gráficas de los mismos. Se proveen vistas que ayudarán al desarrollador a interpretar los resultados de una manera más intuitiva. Una de las contribuciones a este aspecto es una visualización de estilo heatmap [9] para representar las zonas (componentes) de un proyecto más afectadas por smells, detallando el nivel de acoplamiento de estas con el resto de la arquitectura para asistir al desarrollador en la toma de decisiones para el refactoring. La idea de esta vista es enfocar la atención del desarrollador en los puntos críticos del sistema que puedan necesitar de un refactoring, de esta manera tratando las zonas más afectadas es posible lograr un cambio significativo en el refactoring.
componentes de un sistema a distintos niveles de granularidad que se relacionan según una determinada topología, y cuya relación podría ayudar a identificar problemas en el diseño arquitectónico.
Las aglomeraciones son útiles para determinar si existe una relación entre los code smells que afectan a un componente o a una jerarquía de componentes de un sistema. Los problemas arquitecturales de un sistema suelen verse reflejados más comúnmente en las aglomeraciones que en los smells por separado [7]. Esto se debe a que en ocasiones se vuelve difícil establecer y caracterizar una relación entre los smells y sus contrapartes arquitecturales. Un componente de la arquitectura de un sistema generalmente es implementado por varios elementos en el código, por lo que el objetivo de identificar problemas arquitecturales puede no ser llevado a cabo aislando y analizando individualmente las anomalías en el código. A su vez es probable que un problema arquitectural afecte varios elementos de la implementación, por lo que puede ser la presencia de dos o más code smells lo que sirva como un mejor indicador de que existe un problema en la arquitectura, e incluso puede ser un factor determinante la relación que exista entre dichas anomalías.
El objetivo de proveer una vista para las aglomeraciones es reducir parte de la dificultad que conlleva relacionar los problemas detectados en el código del sistema con la arquitectura del mismo otorgando cierta cercanía a estos aspectos para asistir al desarrollador con una perspectiva más clara de los resultados obtenidos. Dada la naturaleza de las aglomeraciones, esta vista enriquecerá la forma en que el desarrollador percibe las relaciones entre los smells facilitando el análisis posterior a los resultados.
1.4 Organización del documento
En el capítulo 2, se introducirá al lector en el estado del arte actual y conceptos utilizados en el resto del documento.
En el capítulo 3, se hará una breve reseña de trabajos relacionados que abordan problemáticas similares a las que se plantean en este trabajo
En el capítulo 4, se tratará el problema de mejora en la performance de la herramienta JSpIRIT. Se hará un análisis del flujo de ejecución y en base a los resultados obtenidos se propondrá una solución al problema.
En el capítulo 5, se tratará el problema de mejora de la visualización de resultados en la herramienta JSpIRIT y se propondrán vistas que asistan a la visualización de resultados original de la herramienta.
En el capítulo 6, se realizará un análisis empírico que evaluará la mejora de performance propuesta en el capítulo 4 desde diversos aspectos como los tiempos de respuesta y consumo de memoria.
Finalmente, en los capítulos 7 y 8, se estudiarán las mejoras de visualización propuestas en el capítulo 5 mediante la ejecución de la herramienta sobre dos aplicaciones, ilustrando el uso de las vistas y demostrando su utilidad como parte del análisis.
Capítulo 2
Evolución de los sistemas
Una vez que los sistemas son desarrollados y puestos en funcionamiento, su utilidad comenzará a degradarse progresivamente debido a los cambios en el entorno [25]. El hecho de que un sistema se vuelva menos útil progresivamente causa su “envejecimiento” [28] y la necesidad de evolucionar añadiendo nuevos requerimientos, preservando la simplicidad y extensibilidad del software. En este contexto, la evolución del software se define como el proceso de cambio de un sistema para cumplir con nuevos requerimientos a lo largo de su vida.
Este capítulo servirá de introducción al lector para que este se familiarize con los conceptos utilizados a lo largo de este trabajo.
2.1 Mantenimiento de los sistemas
Los sistemas de software evolucionan constantemente debido a los cambios en el entorno en el que se ejecutan. Los cambios en el entorno pueden causar la aparición de modificaciones en los requerimientos, creación de nuevas funcionalidades y cambios en las reglas de negocios entre otros. Si los sistemas no se adaptan, su utilidad se degradará con el paso del tiempo [51].
El mantenimiento es necesario para evitar el envejecimiento del software y facilitar la evolución de los sistemas. El mantenimiento del software se define como:
● El proceso de modificación de un sistema o componente luego de su entrega para corregir fallas, mejorar performance u otros atributos, o adaptación a un entorno cambiante [23].
una mejora. El objetivo es modificar el producto de software existente preservando su integridad [24].
Las actividades de mantenimiento se categorizan en 4 clases [26]:
● Adaptativo: Es la modificación del sistema realizada luego que éste fue entregado con el fin de mantenerlo operativo debido a un ambiente cambiante o que ya ha cambiado.
● Perfectivo: Es la modificación del sistema realizada luego que éste fue entregado con el fin de mejorar su performance o mantenimiento.
● Correctivo: Es la modificación reactiva del sistema realizada luego que éste fue entregado con el objetivo de corregir fallos.
● Preventivo: Es la modificación proactiva del sistema con el fin de evitar problemas en el futuro.
Diversas investigaciones muestran que aproximadamente el 75% de las actividades de mantenimiento son del tipo adaptativo y perfectivo [26, 27, 29]. Por lo tanto, la adición de nuevos requerimientos de usuario o la modificación de los existentes son los principales problemas de la evolución y mantenimiento de software [18].
Los problemas de evolución y mantenimiento son usualmente explicados con la metáfora de “deuda técnica”, que es similar a una deuda financiera. En esta metáfora, todas las malas decisiones de diseño o código fuente pobremente escrito, pospuesto por los desarrolladores para ser arreglado en un futuro, es considerado una deuda [20]. La deuda técnica ayuda a identificar los resultados invisibles de las decisiones pasadas sobre una aplicación que afectan negativamente a su futuro [40]. Esto es debido a que por cada deuda técnica el desarrollador deberá pagar interés, al igual que en una deuda financiera. En este caso, la deuda técnica está asociada con la dificultad extra que surge al modificar el código fuente o agregar nuevos requerimientos. La deuda es saldada solo si se corrige el código problemático que la originó.
extendidos, adaptados o reestructurados para evolucionar, algunos de los siguientes problemas emergen [21]:
1. Los desarrolladores originales no están disponibles.
2. Se utilizaron técnicas de programación o lenguajes obsoletos. 3. Grandes cambios fueron aplicados sin sopesar el diseño original. 4. La documentación está incompleta o no existe.
5. No existen casos de test.
Una vez que la estructura del sistema es entendida, se deben identificar los elementos clave del sistema que deben ser modificados. Para esto es utilizada generalmente la ingeniería inversa [83]. La ingeniería inversa es el proceso de analizar un sistema para identificar sus componentes e interrelaciones y así crear representaciones del sistema basadas en distintas formas o niveles de abstracción superiores. Para este cometido, el desarrollador puede utilizar toda la información que encuentre disponible: código fuente, comentarios en el código, o la documentación de la arquitectura.
Cuando el sistema es entendido, nuevos requerimientos o reestructuraciones pueden ser llevadas a cabo a través de la reingeniería [19]. La reingeniería es la examinación y modificación de un sistema para ser reconstituido en su nueva forma y la subsecuente implementación de dicha forma. La incorporación de requerimientos conlleva al agregado de nuevo código fuente y/o a la reestructuración del código existente, teniendo en cuenta la mantenibilidad y modularización del código resultante. Para lograr esto, las técnicas de refactoring [3] pueden ser utilizadas con el objetivo de mejorar el diseño del sistema, simplificando la adopción de nuevos requerimientos.
JSpIRIT utiliza tres estrategias para analizar la criticidad de un smell [4]: 1. Historia: el historial de versiones de un sistema puede ayudar a encontrar aquellos elementos (clases o paquetes) que son más propensos a cambiar. El historial también ayuda a visualizar los puntos en los cuales ocurrieron grandes cambios o se introdujeron bugs.
2. Escenarios de modificabilidad: El análisis de escenarios ayuda al desarrollador a entender cuán modificables pueden ser elementos específicos del sistema desde un punto de vista arquitectural.
3. Tipo de smell: los smells son antipatrones en el código fuente que permiten al desarrollador identificar elementos del código que deben ser mejorados. En este sentido, los code smells ayudan a los desarrolladores a lograr un entendimiento más profundo del sistema a través de la detección de problemas en el diseño.
2.2 Code Smells
Basada en la identificación de elementos que deben ser mejorados, el uso
de code smells [3] es una estrategia que puede ayudar a los desarrolladores a adquirir un mejor entendimiento de los sistemas. Un code smell es un síntoma en el código fuente del sistema que ayuda a identificar un problema de diseño. Los smells permiten a los desarrolladores detectar fragmentos de código que deberían ser reestructurados, para de esa manera mejorar la calidad del sistema. En este aspecto los smells actúan como antipatrones de diseño que ponen en evidencia estas debilidades del sistema. Por esta razón, los smells son una fuente de deuda técnica que debe ser saldada.
La existencia y aumento de code smells en el código genera una degradación en la calidad del software, impactando fuertemente sobre la comprensión, mantenibilidad y evolución del código fuente [15]. Por esto, la manera de eliminar la presencia de smells, es mediante la refactorización del código. Además, conociendo que la existencia de smells produce una degradación en la calidad del código, orientar la refactorización a eliminar la presencia de este tipo de síntomas garantiza la obtención de mejoras sustanciales en el código fuente.
Cada smell puede afectar a varios elementos (ej: paquetes, clases, métodos) de un sistema.
● Código duplicado: Este problema sucede cuando se repite mucho código en la misma clase. El código duplicado lleva a programas difíciles de modificar. Para solucionarlo se deben eliminar las líneas de código que son exactamente iguales y se repiten en varias ocasiones. También se deben eliminar las líneas de código que poseen estructuras similares utilizando las técnicas que provee la programación orientada a objetos como lo son el polimorfismo, la herencia, la abstracción de datos y la modularización.
● Métodos muy largos: Cuando los métodos son extremadamente largos se dificulta mucho su entendimiento, volviéndose difícil agregar funcionalidad o encontrar errores. Las clases que contienen a estos métodos se vuelven complejas de mantener. En este caso hay que dividir el código en porciones y extraerlas para crear métodos más pequeños, que sean más fáciles de mantener, reusar y comprender.
● Clases muy grandes: Este resulta un caso similar al anterior descrito, pero puede significar una mala división de responsabilidad de los objetos. Las clases con tamaño fuera de lo normal llevan a un sistema que es complejo de entender y extender. Se debe tratar de identificar qué cosas realiza esa clase y ver si realmente esas cosas se encuentran relacionadas. Si no es así, hacer clases más pequeñas, con una correcta división de la funcionalidad.
● Métodos que necesitan muchos parámetros: Cuando hay alto acoplamiento entre métodos, se requieren pasar como parámetro muchas variables. Este problema tiene como resultado un método poco mantenible. Se puede solucionar realizando una clase que contenga esos parámetros y utilizar esa clase en el pasaje por parámetros. Esto se realiza, en especial, cuando los parámetros están fuertemente relacionados o suelen utilizarse juntos.
● Instrucciones Switch Case: Normalmente, una sentencia de este tipo, se debe repetir en varios sitios del código, aunque en cada uno de ellos se realicen actividades distintas. Existen formas, utilizando el polimorfismo, que evitan tener que realizar esta repetición de código o incluso evitan que sea necesario usarlo.
A través del uso de code smells, varios problemas como métodos con alta complejidad o excesivas dependencias entre clases pueden ser identificados.
2.2.1 Tipos de Code Smells
Algunos catálogos populares de smells son los presentados por Fowler [3] y LanzaMarinescu [8]. En la tabla 2.1 se resumen los smells presentados por [8], algunos de los cuales también están presentes en [3].
Tipo de Code Smell Descripción
God Class Hace referencia a clases que tienden a centralizar la funcionalidad de un sistema entero. Una God Class posee demasiadas responsabilidades, utilizando datos de otras clases y delegando sólo pequeñas tareas a otras clases. Impacta en la reusabilidad y comprensibilidad del sistema.
Brain Class Tiende a centralizar la funcionalidad de un sistema en varios de sus métodos. Se diferencia de God Class por no acceder a datos de otras clases de manera abusiva. No llegan a ser God Class, pero poseen al menos un Brain Method.
Feature Envy Se refiere a métodos que están más interesados en datos provenientes de otras clases que de su propia clase.
Brain Method Brain Method tiende a centralizar la funcionalidad de una clase en un método de la misma manera que God Class lo hace con un sistema.
Data Class Se trata de clases contenedoras de datos pero con muy poca funcionalidad o nula. Significant Duplication Hace referencia a código duplicado que
Dispersed Coupling Un método con demasiadas dependencias que se encuentran dispersas en muchas clases. Los métodos invocados suelen ser muy simples. Intensive Coupling Se trata de un método con demasiadas dependencias que se encuentran concentradas en una o pocas clases. Shotgun Surgery Hace referencia a cuando el cambio en un método genera muchos pequeños cambios en otros métodos y clases. Se trata de la situación inversa al Dispersed Coupling. Refused Parent Bequest Se identifica este Code Smell cuando, en una jerarquía, un padre define métodos protegidos que no son utilizados por sus hijos. Esto sugiere que algo es incorrecto en la clasificación de relación entre clases. Tradition Breaker Hace referencia a una clase que no especializa la clase de la cual hereda, y sólo agrega nuevos servicios que no dependen demasiado de la funcionalidad heredada. Esto es señal de que algo es incorrecto, ya sea con la interfaz que implementa la clase hijo o con la clasificación de la relación entre clases. Tabla 2.1: Code Smells catalogados por Lanza y Marinescu.
2.3 Aglomeraciones
En [30] se presenta un estudio acerca de la relación entre las aglomeraciones y los problemas arquitecturales en un sistema.
más smells de código puede ser establecida a través de un método compartido por dichos smells. Esto es, un grupo de dos o más smells pueden simultáneamente afectar al mismo método. Por lo tanto, en este caso, se considera que este grupo de code smells (afectando al mismo método) es una aglomeración.
Además, en [30] se realiza a su vez una categorización de aglomeraciones basada en sus topologías y considerando sus características particulares, y se presenta un metamodelo que caracteriza el concepto de aglomeración y relaciona el concepto con otros relevantes al estudio. En este metamodelo (Figura 2.1) cada topología es asociada con una estrategia de detección. A su vez, cada estrategia es usada por solo una topología. Cada estrategia considera diferentes tipos de elementos y relaciones. Los tipos de elementos pueden ser elementos de código (métodos y clases) o componentes arquitecturales. Los tipos de relaciones pueden ser jerárquicas, de dependencia o concerns en común.
Figura 2.1: Metamodelo para las aglomeraciones
La siguiente es la categorización de los tipos de topología que sugiere [30]:
1. Intraboundary 2. Crossboundary 3. Hierarchical 4. Concernbased
El estudio se concentra en estas cuatro topologías debido a que son fáciles de entender y describir, y además pueden ser detectadas automáticamente (y de manera confiable) con herramientas de asistencia existentes [31].
2.3.1 Tipos de aglomeraciones
Intraboundary
Es una aglomeración compuesta por elementos de código anómalos que implementan el mismo componente de la arquitectura. Caen dentro de esta topología las aglomeraciones localizadas dentro de un mismo componente y compuestas por: (i) elementos de código anómalo internos que están sintácticamente relacionados o (ii) elementos de código anómalo internos infectados por el mismo tipo de smell. En ambos casos, ninguno de los elementos de código toma parte en la implementación de un componente de la arquitectura diferente. En el segundo caso, no hay una relación sintáctica entre los elementos. Para entender lo que se considera como relación sintáctica, se asumen dos clases, T y X, y dos métodos M y N. Las clases T y X están sintácticamente relacionadas si X es referenciada dentro de T o vice versa. Los métodos M y N están sintácticamente relacionados si M llama a N o vice versa. La figura 2.2 muestra un ejemplo de una aglomeración intraboundary. En este caso, el método createHandler es infectado por “Divergent Change” y el método parse es infectado por “Long Method”.
Como ambos se encuentran dentro del mismo componente arquitectural
(webgrid) y hay una asociación entre ellos, los elementos de código
createHandler y parse forman una aglomeración intraboundary.
Crossboundary
Es una aglomeración compuesta por elementos de código anómalo relacionados sintácticamente, pero localizadas en la implementación de componentes de arquitectura diferentes. Las relaciones sintácticas consideradas son las mismas que para las aglomeraciones del tipo intraboundary. Una aglomeración del tipo crossboundary siempre involucra dos o más componentes. Puede haber más de un elemento de código anómalo en cada uno de los componentes involucrados. Sin embargo, debe haber al menos un elemento de código anómalo dentro de cada componente de la aglomeración (dos o más). A modo de ejemplo en la Figura 2.3 se representa una vista parcial arquitectónica de 4 componentes:
metadata, crawler, filemgr y pushpull. Los smells identificados en los elementos del programa son representados por sus iniciales en círculos descritos en la leyenda. El componente metadata encierra, entre otros, un elemento de código anómalo (Metadata), que es “usado” por tres elementos externos anómalos (ProductCrawler, RemoteFile y XmlRpcFileManager). Como los elementos anómalos son internos a componentes diferentes, forman una aglomeración que cruza las fronteras de estos.
Hierarchical
Es una aglomeración compuesta por elementos de código anómalo que son parte del mismo árbol de herencia. Las jerarquías pueden ocurrir tanto en la implementación de un único componente como así también a través de múltiples componentes. En esta topología se consideran sólo jerarquías donde los elementos de código están infectados por el mismo tipo de smell. La introducción recurrente del mismo smell en diferentes elementos de código puede representar un problema mayor en la jerarquía. Un ejemplo de esta topología de aglomeración se encuentra en la jerarquía Versioner (Figura 2.4). Todas las subclases de Versioner están afectadas por los mismos smells – Divergent Change, Feature Envy, Long Method and Shotgun Surgery – por lo que pueden ser agrupadas en una aglomeración jerárquica.
Figura 2.4: Aglomeración tipo Hierarchical.
2.4 JSpIRIT
La herramienta utilizada como base para la realización de este trabajo se denomina JSpIRIT (Java Smart Identification of Refactoring opportunITies) [4], y se trata de una herramienta semiautomatizada que analiza el código de un sistema en busca de potenciales problemas de diseño en el mismo, y tiene como objetivo asistir al desarrollador en el proceso de refactoring en sistemas orientados a objeto. Para esto la herramienta hace uso de diferentes estrategias que permiten detectar anomalías en el código como pueden ser los code smells [4] y las aglomeraciones [7]. Los autores de JSpIRIT proponen un approach que identifica los code smells y aglomeraciones más críticos del sistema y bajo diferentes criterios seleccionables permite generar un ranking de los mismos. El uso de múltiples criterios ayuda a examinar los smells desde diferentes perspectivas y de esta manera descubrir si el smell es un problema crítico. Los criterios de priorización de smells que permite utilizar JSpIRIT están relacionados con:
1. La relación de los smells con la arquitectura.
2. La importancia del tipo de smell.
3. La probabilidad de que el código fuente relacionado con el smell sea modificado en futuras versiones del sistema.
Estos criterios fueron elegidos porque toman en cuenta: la estabilidad del componente en el que fué encontrado el smell, la importancia que le otorga el desarrollador a cada tipo de smell, y además, permiten al desarrollador enfocarse en aquellas partes del sistema que afectan la calidad de la arquitectura a través del análisis de requerimientos específicos de modificabilidad que deben ser satisfechos. Sumado a esto está el hecho de que estos criterios son a su vez complementarios.
Envy, God Class, Intensive Coupling, Refused Parent Bequest, Shotgun Surgery
y Tradition Breaker.
La herramienta provee además soporte para detección de 2 tipos de aglomeraciones: (i) “IntraBoundary” y (ii) “Hierarchical” [7]. A su vez JSpIRIT divide al tipo (i) en 3 subtipos según la granularidad de los elementos de código afectados por la aglomeración:
● IntraComponent (El elemento afectado es un paquete) ● IntraClass (El elemento afectado es una clase)
● IntraMethod (El elemento afectado es un método)
Mientras que en el tipo (ii) el elemento afectado siempre será una superclase o interfaz y sus especializaciones o implementaciones respectivamente.
Además, las aglomeraciones pueden ser rankeadas utilizando criterios similares a los utilizados para la priorización de smells.
JSpIRIT se instala como un plugin del IDE Eclipse y posee una interfaz 3 mediante vistas acopladas a las convencionales que ofrece el IDE (Figura 2.5). Desde la interfaz es posible configurar todos los aspectos referentes al comportamiento del análisis de code smells, así como también acceder a los elementos de código afectados por smells a través de los resultados.
Figura 2.5: Interfaz gráfica de JSpIRIT.
Capítulo 3
Trabajos Relacionados
Como los requerimientos de un software pueden cambiar a través del tiempo, el código fuente cambia para dar lugar a la implementación de nuevas funcionalidades. Mientras más mantenible es el software, menos trabajo tomará esta tarea. De manera que es deseable tener un código libre de smells. Para lograr esto un desarrollador puede usar un detector de smells o hacerlo manualmente. Se ha comprobado que es más eficiente el uso de una herramienta para esta tarea [16]. En su mayoría los enfoques que utilizan estas herramientas están centralizados en code smells, ya que son problemas de código recurrentes que dificultan el entendimiento, mantención y evolución de los sistemas.
Existen numerosos experimentos [39, 40, 41] que ponen a prueba la efectividad de algunas de las herramientas más conocidas para la detección de code smells, aún así el objetivo de este capítulo es meramente repasar las características principales y destacar ciertos aspectos que en general son deseables en dichas herramientas.
3.1 Factores de éxito
En [38] se definen ciertos factores que tienen una influencia positiva en el éxito de las herramientas de detección automática de code smells. Algunos de los cuales fueron tomados en cuenta para el desarrollo de este trabajo.
3.1.1 Usabilidad
En [37, 16] se plantean ciertos lineamientos (Tabla 3.1) relacionados con la usabilidad que son deseables en herramientas de detección de code smells y cuya relevancia es evaluada experimentalmente en [38] donde se toma como hipótesis que las herramientas que siguen estas reglas generales son más exitosas en este aspecto.
Limitación La herramienta no debe agobiar al desarrollador con los smells que detecta.
Relacionalidad Cuando se muestran detalles acerca de los code smells, la herramienta debería mostrar las relaciones entre los elementos del sistema afectados.
Preferencia La herramienta debería enfatizar los smells que son difíciles de encontrar sin herramientas de soporte.
No distracción La herramienta no debe distraer al desarrollador.
Estimabilidad La herramienta debería ayudar a estimar la extensión de un smell en el código.
No obstrucción La herramienta debería permitir al programador realizar otros trabajos mientras se realiza el análisis.
Lucidez Además de encontrar smells, la herramienta debería hacer saber al programador por qué determinados smells existen.
Tabla 3.1: Reglas generales para las herramientas de detección de smells.
3.1.2 Continuidad
En [3] se propone usar la “Metáfora de los dos sombreros” que significa el cambio constante entre dos actividades: agregar funcionalidad y refactorizar. Este tipo de refactoring se denomina “root canal refactoring”. Por el contrario, existe el “floss refactoring” en el cual no se distingue entre el agregado de funcionalidad y el refactoring, sino que se propone el refactoring a medida que se agrega el código. Se ha argumentado que el “root canal refactoring” es ineficiente [42]. Cuando se usa este tipo de refactoring el desarrollador debe primero examinar el código para volver a entender el contexto, y de esa manera ser capaz de reconocer una oportunidad de refactoring. Esta tarea se vuelve engorrosa y consume tiempo. En contraste, cuando se utiliza “floss refactoring” el desarrollador ya se encuentra en el contexto del código a refactorizar.
Para que el refactoring sea eficiente y tolerable, debe ser aplicado de manera continua. Visualizar los smells y bugs en el contexto actual de programación alienta al desarrollador a refactorizar continuamente. De manera que los detectores de smells deberían soportar al programador continuamente con información de detección de smells y propuestas de refactoring.
3.1.3 Interpretación
Muchas herramientas existentes calculan métricas de código. Pero unas pocas puede interpretar las métricas y reaccionar ante esas interpretaciones. Una característica faltante entre la mayoría de los detectores de smells es la capacidad de realizar refactorings directamente como una solución a un smell detectado o al menos de proponer uno. Esto puede no ser posible para todos los smells, pero si para los más sencillos como por ejemplo “Duplicated Code” [3]. En este caso el refactoring propuesto sería “Extract Method” o “Extract Class”.
3.1.4 Especificidad
Además de las herramientas de detección convencionales, están los detectores de malas prácticas. Estos detectores son específicos a un lenguaje de programación, ya que cada lenguaje tiene sus propias malas prácticas.
Figura 3.1: Mala práctica encontrada por el plugin Findbugs para Eclipse.
Por ejemplo en la Figura 1 el plugin Findbugs para el IDE Eclipse ha 4 encontrado una mala práctica de Java: Usar concatenación de strings convencional en un ciclo que implica una baja de performance. En reemplazo es recomendado utilizar el tipo StringBuffer. Esto es un inconveniente en Java ya
que en otros lenguajes el operador “+=” está sobrecargado para utilizar un
StringBuffer o similar por detrás.
Como se puede ver, los detectores de malas prácticas no solo ayudan a mejorar la calidad del código sino también en ocasiones a mejorar la performance. Además poseen gran potencial para mejorar el código, ya que las reglas de detección están adaptadas a lenguajes de programación específicos.
3.1.5 Integración
La mayoría de las herramientas de métricas o detección de code smells son aplicaciones standalone o plugins. Las aplicaciones standalone tienen la desventaja de complicar el cumplimiento de los factores de continuidad e interpretación explicados en 3.1.3, y 3.1.2. Para poder realizar sugerencias de refactoring la herramienta necesita integración visual con el IDE. La continuidad no puede lograrse debido a que una aplicación standalone nunca sabría qué pieza de código el desarrollador está editando. Deployado como un plugin un detector puede mostrar continuamente si se encontraron nuevos smells sin forzar al programador a tener que dejar la IDE. Cuando un smell es encontrado la herramienta puede fácilmente mostrar su presencia y proponer como eliminarlo. Este comportamiento también hace énfasis en el factor de usabilidad, introducido en la sección 3.1.1.
3.2 Herramientas
En esta sección se describen algunas de las herramientas que se encuentran actualmente disponibles para la detección automática de code smells y se detallan comparativas de algunas de sus características (Tabla 3.2), así como también los diferentes smells detectados por cada una (Tabla 3.3).
Herramienta Tipo Lenguajes Refactoring Indicadores en código
Checkstyle Eclipse plugin, Standalone
Java No Si
DECOR Standalone Java No No
iPlasma Standalone C++, Java No No
inFusion Standalone C, C++, Java No No
JDeodorant Eclipse plugin
Java Si Si
PMD Eclipse plugin, Standalone
Java No Si
Stench Blossom
Eclipse plugin
Java No Si
Tabla 3.2: Herramientas de detección de code smells.
los IDE modernos generalmente son capaces de realizar ciertos refactorings automáticamente. Sería deseable al menos que las herramientas ayuden al usuario a entender la causa de los smells y que no muestren la información de manera que agobie al desarrollador en casos donde los smells proliferan como se explicó en la sección 3.1.
Checkstyle
Checkstyle fue desarrollada para ayudar a los programadores a escribir código5 Java que adhiere a un estándar de codificación. Es capaz de detectar los smells “Large Class”, “Long Method”, “Long Parameter List”, y “Duplicated Code”.
DECOR
En [43, 44] se define un approach que permite la especificación y detección automática de smells de código y de diseño (también llamados antipatrones). Se especifican seis smells usando un lenguaje personalizado, se generan automáticamente sus algoritmos de detección mediante templates, y se validan dichos algoritmos en términos de precisión y exactitud. Este approach es implementado en la plataforma “Decor platform for software analysis” . 6
inFusion
inFusion es la evolución comercial de iPlasma. Es capaz de detectar más de 207 defectos de diseño y smells, como “Duplicated Code”, clases que rompen la encapsulacion (Data Class, God Class), métodos y clases fuertemente acoplados, o jerarquías de clases con diseños defectuosos.
iPlasma
Esta herramienta [45] es una plataforma integrada para la evaluación de la calidad de los sistemas orientados a objetos que incluye soporte para todas las fases de análisis, desde extracción de modelos, hasta análisis basados en
5 http://checkstyle.sourceforge.net/
6 http://www.ptidej.net/download
métricas de alto nivel. iPlasma es capaz de detectar lo que los autores definen 8 como “desarmonías” de código, clasificadas en desarmonías de identificación, desarmonías de colaboración y desarmonías de clasificación. La descripción detallada de estas se puede encontrar en [8]. Varios smells son considerados desarmonías, por ej: Duplicated Code, God Class, Feature Envy, y Refused Parent Bequest.
JDeodorant
JDeodorant [15] es un plugin para Eclipse que identifica automáticamente los smells Feature Envy, God Class, Long Method y Switch Statement (en su variante “Type Checking”) en código Java . La herramienta asiste al usuario en 9 determinar una secuencia apropiada de refactorings determinando las posibles transformaciones que resuelven los problemas identificados, rankeandolos según su impacto en el diseño, presentandolos al desarrollador, y aplicando automáticamente la escogida por este.
PMD
PMD10 escanea el código fuente Java y busca por potenciales problemas o posibles bugs como código muerto, bloques try/catch/finally/switch vacíos, variables locales o parámetros no utilizados, y código duplicado. PMD puede detectar los smells Large Class, Long Method, Long Parameter List, y Duplicated Code, y permite al usuario configurar umbrales para las métricas.
Stench Blossom
Stench Blossom [16] es un detector de smells que provee un entorno de visualización interactiva diseñado para dar a los desarrolladores un vistazo rápido y de alto nivel de los smells en el código y su origen. La herramienta es un plugin para el IDE Eclipse que provee a los programadores tres diferentes vistas, que ofrecen más información sobre los smells visualizados. El feedback es sintético y visual, y tiene la forma de un conjunto de pétalos cercanos al
8 http://loose.upt.ro/iplasma/index.html
9 http://http://www.jdeodorant.com/
elemento de código en el editor del IDE. El tamaño del pétalo es directamente proporcional a la “fuerza” del smell del elemento de código al que refiere. El único procedimiento posible para encontrar smells es revisar el código fuente, buscando por pétalos lo suficientemente grandes como para suponer que existe un code smell en esa parte del código. La herramienta es capaz de detectar 8 smells diferentes.
Tabla 3.3: Soporte para code smells.
Otras herramientas
que puede detectar varios smells, esencialmente siguiendo las reglas de detección de [8]. inCode es un plugin para Eclipse basado en inFusion, que 11 provee detección de problemas de diseño a medida que el código es escrito. Otras herramientas son: FxCop para .NET, Analyst para Java (comercial), JCosmo [47] (para Linux), CloneDigger y ConQat.
3.3 Técnicas de visualización
Dado que uno de los propósitos de este trabajo es proponer una mejora de visualización para la herramienta JSpIRIT, en esta sección se destacan algunas técnicas de visualización desarrolladas aplicadas a la evolución de los sistemas en general y en algunos casos particularmente a los code smells. Algunos de estos approaches provienen de fuentes investigativas mientras que otros se encuentran implementados en herramientas comerciales.
(a) “In the small” (b) “In the large”
Figura 3.2: Técnicas de visualización de D’Ambros y Lanza.
D’Ambros y Lanza [9] presentan visualizaciones para analizar la evolución de un sistema basadas en sus diferentes versiones. Los autores presentan dos grupos de visualizaciones i) “in the large”, y ii) “in the small”. La primera se enfoca en identificar cambios y diferentes commits a una clase usando figuras de tiempo discretas (Figura 3.2a). Mientras que la última se enfoca en encontrar bugs en las clases y analizar el tamaño y la complejidad de las clases (Figura 3.2b). Por ejemplo, la vista de la Figura 3.2a puede ayudar a identificar las clases que sufrieron más cambios mientras que la vista en la Figura 3.2b puede ayudar a identificar clases largas y complejas como las “God Classes”. Los mismos autores proponen una técnica de visualización para calcular acoplamiento entre módulos, correlación entre clases y complejidad de clases [48].
Figura 3.3: Matriz de evolución.
cantidad de métodos y cantidad de variables de instancia). Este trabajo también propone una categorización de las clases visualizadas en la matriz. Estas categorías son patrones encontrados a través de la historia y son basados en el tamaño de las clases, el porcentaje de modificaciones entre versiones, y la creación o eliminación de clases (Figura 3.3). Mientras que estos patrones pueden ayudar a identificar numerosos problemas en el código, la supervisión del desarrollador es necesaria dado el alto porcentaje de falsos positivos.
Figura 3.4: Vista de entorno de la herramienta Stench Blossom.
Los creadores de la herramienta Stench Blossom [16] optaron por un approach en código en tiempo real para la visualización de code smells. La visualización (Figura 3.4) muestra smells relacionados con el contexto actual en el que se está trabajando. Los autores argumentan que el hecho de que la herramienta esté disponible constantemente y bajo el contexto actual de trabajo hacen a la herramienta apropiada para el “floss refactoring” [3] donde los programadores frecuentemente cambian entre tareas de refactorización y otro tipo de modificaciones en el código.
Este enfoque es apropiado para resolver los smells que se presentan en el código, sin embargo, la visión del desarrollador puede verse limitada ya que solo se tiene en cuenta el código que se está viendo para realizar un refactoring. Como se verá en este trabajo muchas veces smells del mismo tipo se presentan en varias clases de un mismo componente del sistema, y en ocasiones se extienden hacia otros componentes formando aglomeraciones, estas aglomeraciones pueden implicar problemas mayores en el diseño del sistema que requieren ser observados desde una perspectiva más amplia para su análisis.
Figura 3.5: Vista de mapa de paquetes de la herramienta inCode.
que corresponde a cada clase representa la cantidad de atributos y cantidad de métodos en las mismas respectivamente. La vista también es configurable para mostrar el nivel de acoplamiento entre clases.
Esta herramienta implementa visualizaciones intuitivas detallando los componentes del sistema y su nivel de afectación. Por desgracia, las visualizaciones excluyen las dependencias entre paquetes, que son de utilidad para entender la estructura del sistema y en ocasiones pueden poner en evidencia problemas en el diseño producidos por una violación en la arquitectura del sistema.
Algunos autores [17] argumentan que la utilización de métricas automatiza el proceso de búsqueda de smells pero produce resultados voluminosos, imprecisos e incongruentes. Sostienen que las visualizaciones tradicionales muestran de manera eficaz la distribución de métricas del código de un sistema. Pero cuando los falsos positivos pueden alcanzar un 90%, los usuarios necesitan más detalles para entender los problemas.
Dichos autores también aseguran que aunque se ha intentado en varios trabajos la visualización de code smells, estos enfoques soportan pocos smells, fallan en advertir las imprecisiones de las métricas, y sus visualizaciones no fueron diseñadas con las técnicas de inspección de software [50] en mente. Además sostienen que estos enfoques también fallan a pequeñas escalas. Están pensados para una vista general del sistema y no son capaces de ilustrar un problema de diseño particular.
Figura 3.6: Health screen construido con simbología básica (Figura 3.7).
Figura 3.7: Simbología o bloques básicos de la visualización propuesta en [17].
Un aspecto positivo de este enfoque es que la creación del panel utilizado como visualización no utiliza gran cantidad de recursos computacionales. Por otro lado, la simbología utilizada (Figura 3.7) permite plasmar gran cantidad de información en el panel, lo que puede ser de gran utilidad, pero a la vez puede ser contraproducente haciendo difícil su comprensión. Como se verá más adelante en este trabajo, uno de los problemas que se enfrentan al diseñar una visualización de este tipo es la cantidad de datos que se deben bosquejar, y cómo ordenarlos y agruparlos para que el desarrollador no se vea abrumado ante la cantidad de información que se le ofrece. Es primordial que el desarrollador sea capaz de procesar y relacionar fácilmente la información que la visualización le entrega.
Capítulo 4
Optimización: Performance
Uno de los objetivos de este trabajo es mejorar la performance de los análisis de código, cuya demora se traduce en un degradamiento en la usabilidad.
La performance de un sistema puede ser medida utilizando diversos atributos [73]. Algunos de estos atributos (ej: throughput, carga de trabajo) están orientados a diferentes tipos de sistemas, como los sistemas distribuidos [74] o sistemas que presentan un determinado diseño arquitectónico, mientras que otros (ej: utilización de recursos, tiempos de respuesta) aplican a todos los sistemas en general.
Como se explicó anteriormente JSpIRIT se ve afectado por altos tiempos de respuesta que degradan la usabilidad de la herramienta, por lo que la aceleración de estos tiempos es un aspecto a optimizar.
Según el “IBM Dictionary of Computing” [52] el tiempo de respuesta se define como el tiempo transcurrido entre el fin de una petición o demanda a un sistema y el principio de una respuesta del sistema a dicha petición.
En general es deseable que los tiempos de respuesta de un sistema sean cortos. Cuando existen retrasos que impiden el progreso de una tarea, muchas personas se frustran. Tiempos de respuesta largos producen este tipo de condiciones en usuarios de computadoras, esto conlleva a una mayor frecuencia de errores y baja satisfacción [53].
En el caso de JSpIRIT el usuario es un desarrollador, y la tarea que se lleva a cabo es la del refactoring de un sistema. Si bien se ha probado que el refactoring es una técnica beneficiosa para el desarrollo de software [3] no muchos desarrolladores están dispuestos a otorgar demasiado tiempo a llevarlo a cabo por diversas razones [54], lo que acentúa aún más la necesidad de que nada retarde la realización de dicha tarea.
A los efectos de lo comentado en el transcurso de este capítulo se ahondará en un análisis que permitirá reconocer los puntos problemáticos que afectan a los tiempos de respuesta en JSpIRIT y finalmente se procederá a presentar una solución para subsanarlos.