• No se han encontrado resultados

Análisis y mejoras de usabilidad y performance sobre una herramienta de asistencia en el refactoring de aplicaciones

N/A
N/A
Protected

Academic year: 2020

Share "Análisis y mejoras de usabilidad y performance sobre una herramienta de asistencia en el refactoring de aplicaciones"

Copied!
144
0
0

Texto completo

(1)

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 

(2)
(3)

Índice General 

 

1. Introducción

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   

   

(4)

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 

     

(5)

Índice de Figuras

   

2.1 Meta­modelo para las aglomeraciones . . . 22   

2.2 Aglomeración Intra­boundary . . . .23 

2.3 Aglomeración Cross­boundary . . . .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 

(6)

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 Intra­Component 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 Intra­Class . . . .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)

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   

                               

(8)

Í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

 

 

(9)

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 semi­automatizadas 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 

 

(10)

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 semi­automatizada 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. 

(11)

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 

 

(12)

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. 

(13)

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. 

(14)

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. 

   

       

(15)

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]. 

 

(16)

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ó. 

(17)

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 re­ingenierí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. 

(18)

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 anti­patrones 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.  

(19)

 

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. 

(20)

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 Lanza­Marinescu [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       

(21)

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. 

(22)

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 meta­modelo que caracteriza el concepto de aglomeración y        relaciona el concepto con otros relevantes al estudio. En este meta­modelo        (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. 

(23)

 

Figura 2.1: Meta­modelo para las aglomeraciones 

   

La siguiente es la categorización de los tipos de topología que sugiere        [30]: 

 

1. Intra­boundary  2. Cross­boundary  3. Hierarchical  4. Concern­based   

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]. 

         

(24)

2.3.1 Tipos de aglomeraciones 

 

Intra­boundary 

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 intra­boundary. 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 parse forman una aglomeración intra­boundary. 

 

(25)

Cross­boundary 

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 intra­boundary. Una aglomeración del tipo        cross­boundary 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. 

 

(26)

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.   

(27)

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 semi­automatizada 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. 

(28)

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) “Intra­Boundary”   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: 

Intra­Component (El elemento afectado es un paquete)  ● Intra­Class (El elemento afectado es una clase

Intra­Method (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. 

 

(29)

Figura 2.5: Interfaz gráfica de JSpIRIT. 

   

 

(30)

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. 

(31)

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. 

(32)

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. 

(33)

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 plug­in 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       

(34)

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 stand­alone o plug­ins. Las aplicaciones stand­alone 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 stand­alone nunca        sabría qué pieza de código el desarrollador está editando. Deployado como un        plug­in 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. 

                   

(35)

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. 

 

(36)

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 anti­patrones).        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 

(37)

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/ 

(38)

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

 

(39)

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. 

  

(40)

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.   

(41)

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. 

(42)

 

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. 

(43)

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.    

 

(44)

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. 

(45)

      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. 

(46)

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. 

Referencias

Documento similar

 Para recibir todos los números de referencia en un solo correo electrónico, es necesario que las solicitudes estén cumplimentadas y sean todos los datos válidos, incluido el

puedan adscribirse a un género común, sino que el concepto de sistema político-jurí- dico resulta ser un híbrido de realidades heterogéneas; en segundo lugar, que este ca-

Abstract: This paper reviews the dialogue and controversies between the paratexts of a corpus of collections of short novels –and romances– publi- shed from 1624 to 1637:

entorno algoritmo.

Missing estimates for total domestic participant spend were estimated using a similar approach of that used to calculate missing international estimates, with average shares applied

Habiendo organizado un movimiento revolucionario en Valencia a principios de 1929 y persistido en las reuniones conspirativo-constitucionalistas desde entonces —cierto que a aquellas

The part I assessment is coordinated involving all MSCs and led by the RMS who prepares a draft assessment report, sends the request for information (RFI) with considerations,

Ciaurriz quien, durante su primer arlo de estancia en Loyola 40 , catalogó sus fondos siguiendo la división previa a la que nos hemos referido; y si esta labor fue de