Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Por
Manuel Alonso
Facundo H. Klaver
Bajo la supervisión de
Dr. Andrés Díaz-Pace
Dr. Santiago Vidal
MA
FHK
2
2.5.Testing............................................................................................................................ 29
2.6.Resumen........................................................................................................................ 30
3.Trabajos Relacionados...................................................................................32
3.1.Aspectos generales del refactoring de software........................................................32
3.1.1.Formalización de los procesos de refactoring..........................................................32
3.1.2.Desafíos y beneficios de refactorizar.......................................................................32
3.1.3.Dos tácticas de refactoring.......................................................................................34
3.1.4.Herramientas para cada táctica de refactoring.........................................................34
3.1.5.Refactoring guiado por métricas..............................................................................34
3.2.Enfoques de refactoring...............................................................................................35
3.2.1.Identificación de oportunidades de refactoring.........................................................35
3.2.2.Refactoring basado en herramientas de visualización.............................................35
3.2.3.Refactoring orientado por features...........................................................................36
3.2.4.Modelo matemático para refactoring........................................................................37
3.3.Impacto de refactorings en la calidad del software....................................................37
3.3.1.Efectos del refactoring en la calidad del software....................................................37
3.3.2.Acoplamiento y cohesión como indicadores de buena calidad................................38
3.3.3.Relación entre calidad y refactoring en casos de estudio........................................38
3.3.4.Modificabilidad de arquitecturas de software...........................................................38
3.3.5.Impacto de técnicas de refactoring en las métricas de calidad................................39
3.4.Refactorings guiados por code smells........................................................................39
3.4.1.Evaluación de sistemas a partir del análisis objetivo y el análisis subjetivo.............40
3.4.2.Detección automatizada de code smells..................................................................40
3.4.3.Identificación de Architectural Smells.......................................................................40
3.4.4.Detección de code smells relevantes para la arquitectura.......................................41
3.4.5.Orden apropiado de resolución de code smells.......................................................41
3.4.6.Una experiencia con code smells en Extreme Programming (XP)...........................42
3.5.Resumen........................................................................................................................ 43
4.Proceso de Refactorización...........................................................................44
4.1.Definición del Proceso..................................................................................................44
4.1.1.Refactoring iterativo por etapas...............................................................................45
4.1.1.1.Etapa 1: Análisis e identificación del problema ¿Qué refactorizar?.........................47
4.1.1.2.Etapa 2: Planteo de solución y definición de escenarios.........................................49
4.1.1.3.Etapa 3: Priorización de backlog. ¿Por dónde conviene empezar?.........................50
4.1.1.4.Etapa 4: Implementación de escenario. ¡Refactorizar!.............................................51
4.1.1.5.Etapa 5: Medición de resultados. ¿Es válida la refactorización?.............................53
4.1.1.6.Etapa 6: Evaluación. ¿Seguir refactorizando?.........................................................54
5.Caso de Estudio..............................................................................................55
5.1.SocialGraph, una herramienta para analizar redes sociales.....................................55
5.1.1.Sobre el Análisis de Redes Sociales........................................................................55
3
5.2.Definición del sistema...................................................................................................56
5.2.1.Versiones de SocialGraph.......................................................................................56
5.2.2.Arquitectura actual de SocialGraph.........................................................................56
5.2.3.Funcionalidades de SocialGraph.............................................................................60
5.2.4.Requerimientos de SocialGraph..............................................................................60
5.3.Aplicación del proceso de refactorización a SocialGraph.........................................61
5.4.Iteración 0: Preliminar...................................................................................................61
5.4.1.Análisis general del problema..................................................................................61
5.4.1.1.Anomalía 1: Estructura del código del proyecto.......................................................62
5.4.1.2.Anomalia 2: Acoplamiento entre capas....................................................................62
5.4.1.3.Anomalía 3: Diseño ligado a correos electrónicos...................................................62
5.4.2.Priorización de las anomalías..................................................................................62
5.4.3.Cobertura de test de la aplicación............................................................................64
5.4.4.Observaciones de la Iteración 0...............................................................................65
5.5.Iteración 1: Re-estructuración del Proyecto...............................................................65
5.5.1.Etapa 1: Análisis e identificación del problema........................................................65
5.5.1.1.Documentación del proyecto....................................................................................65
5.5.1.2.Detección de code smells.........................................................................................65
5.5.1.3.Identificación de los problemas en el diseño............................................................66
5.5.2.Etapa 2: Planteo de solución y definición de escenarios..........................................67
5.5.2.1.Definir refactorings a realizar....................................................................................67
5.5.2.2.Especificar Escenarios de Modificabilidad................................................................67
5.5.2.2.1.Escenario 1.1. Mavenizar Proyecto.........................................................................................67
5.5.2.2.2.Escenario 1.2. Reestructurar dependencia con aplicación WordCram...................................68
5.5.2.2.3.Escenario 1.3. Reestructurar dependencia con aplicación GATE...........................................69
5.5.2.3.Agregar al backlog....................................................................................................69
5.5.3.Etapa 3: Priorización de backlog..............................................................................69
5.5.3.1.Nivel de Impacto.......................................................................................................70
5.5.3.2.Nivel de Esfuerzo......................................................................................................70
5.5.3.3.Ubicación en cuadrantes..........................................................................................70
5.5.4.Etapa 4: Implementar Escenarios............................................................................71
5.5.4.1.Escenario 1.1: Mavenizar proyecto..........................................................................71
5.5.4.1.1.Verificar cobertura de test........................................................................................................71
5.5.4.1.2.Implementar Escenario............................................................................................................71
5.5.4.1.3.Ejecutar casos de test..............................................................................................................75
5.5.4.2.Escenario 1.2: Reestructurar dependencia con aplicación WordCram Proyecto.....76
5.5.4.2.1.Verificar cobertura de test........................................................................................................76
5.5.4.2.2.Implementar Escenario............................................................................................................76
5.5.4.2.3.Ejecutar casos de test..............................................................................................................76
5.5.4.3.Escenario 1.3. Reestructurar dependencia con aplicación GATE............................76
5.5.4.3.1.Verificar cobertura de test........................................................................................................76
5.5.4.3.2.Implementar Escenario............................................................................................................77
5.5.4.3.3.Ejecutar casos de test..............................................................................................................78
4
5.5.4.3.4.Notas sobre la implementación de los escenarios...................................................................78
5.5.5.Etapa 5: Medición de Resultados............................................................................79
5.5.5.1.Análisis basado en métricas y documentación.........................................................79
5.5.5.2.Análisis subjetivo y valoración del grupo de desarrollo............................................79
5.5.5.3.Validación de escenarios..........................................................................................80
5.5.6.Etapa 6: Etapa 6: Evaluación...................................................................................80
5.6.Iteración 2: Diseño ligado a correos electrónicos......................................................80
5.6.1.Etapa 1: Análisis e identificación del problema........................................................80
5.6.1.1.Documentación del proyecto....................................................................................80
5.6.1.2.Detección de code smells.........................................................................................82
5.6.1.3.Identificación de los problemas en el diseño............................................................83
5.6.2.Etapa 2: Planteo de solución y definición de escenarios..........................................83
5.6.2.1.Definir refactorings a realizar....................................................................................83
5.6.2.2.Especificar Escenarios de Modificabilidad................................................................83
5.6.2.2.1.Escenario 3.1: Abstraer el modelo para la construcción del grafo...........................................83
5.6.2.2.2.Escenario 3.1.1: Aplicar patrón Strategy a la clase GraphBuilder...........................................84
5.6.2.2.3.Escenario 3.1.2: Abstraer la clase CommunicationEdge de la clase Mail en el modelo..........85
5.6.2.2.4.Escenario 3.1.3: Abstraer la clase PersonVertex de la clase Mail en el modelo.....................85
5.6.2.2.5.Escenario 3.1.4: Abstraer la clase FileVertex y AttachmentEdge de la clase Mail en el modelo.
................................................................................................................................................................ 86
5.6.2.2.6.Escenario 3.1.5: Abstraer la clase TagFinder de la clase Mail en el modelo...........................87
5.6.2.2.7.Escenario 3.1.6: Refactorizar God Class en GraphPersistenceHelper...................................88
5.6.2.2.8.Escenario 3.2: Abstraer la clase Mail en la interfaz gráfica de la herramienta........................88
5.6.2.2.9.Escenario 3.3: Refactorizar sección de código de “importación de datos”..............................89
5.6.2.3.Agregar al backlog....................................................................................................90
5.6.3.Etapa 3: Priorización de backlog..............................................................................90
5.6.3.1.Nivel de Impacto.......................................................................................................90
5.6.3.2.Nivel de Esfuerzo......................................................................................................91
5.6.3.3.Ubicación en cuadrantes..........................................................................................91
5.6.4.Etapa 4: Implementación de escenarios..................................................................92
5.6.4.1.Escenario 3.1.1: Aplicar patrón Strategy a la clase GraphBuilder............................92
5.6.4.1.1.Verificar cobertura de test........................................................................................................92
5.6.4.1.2.Implementar Escenario............................................................................................................92
5.6.4.1.3.Ejecutar casos de test..............................................................................................................94
5.6.4.2.Escenario 3.1.2: Abstraer la clase CommunicationEdge de la clase Mail en el
modelo..................................................................................................................................94
5.6.4.2.1.Verificar cobertura de test........................................................................................................94
5.6.4.2.2.Implementar Escenario............................................................................................................94
5.6.4.2.3.Ejecutar casos de test..............................................................................................................96
5.6.4.3.Escenarios 3.1.3, 3.1.4 y 3.1.5.................................................................................96
5.6.4.4.Escenario 3.1.6: Refactorizar God Class en GraphPersistenceHelper (GPH).........96
5.6.4.4.1.Verificar cobertura de test........................................................................................................96
5.6.4.4.2.Implementar Escenario............................................................................................................96
5.6.4.4.3.Ejecutar casos de test..............................................................................................................97
5
5.6.4.5.Escenario 3.2: Abstraer la clase Mail en la interfaz gráfica de la herramienta.........98
5.6.4.5.1.Verificar cobertura de test........................................................................................................98
5.6.4.5.2.Implementar Escenario............................................................................................................98
5.6.4.5.3.Ejecutar casos de test..............................................................................................................99
5.6.4.6.Escenario 3.3: Refactorizar sección de código de “importación de datos”..............99
5.6.4.6.1.Verificar cobertura de test........................................................................................................99
5.6.4.6.2.Implementar Escenario............................................................................................................99
5.6.4.6.3.Ejecutar casos de test............................................................................................................103
5.6.5.Etapa 5: Medición de resultados............................................................................103
5.6.5.1.Análisis basado en métricas y documentación.......................................................103
5.6.5.2.Análisis subjetivo y valoración del grupo de desarrollo..........................................104
5.6.5.3.Validación de escenarios........................................................................................105
5.6.6.Etapa 6: Evaluación...............................................................................................105
6.Conclusiones.................................................................................................106
6.1.Evaluación de la solución...........................................................................................106
6.2.Aportes y beneficios...................................................................................................107
6.3.Limitaciones................................................................................................................ 108
6.4.Trabajos futuros.......................................................................................................... 109
7.Referencias....................................................................................................110
6
1. Introducción
Una característica intrínseca de los sistemas de software es su necesidad de evolucionar. A
medida que el software se ha mejorado, modificado y adaptado a las nuevas exigencias, el
código se vuelve más complejo y se aleja de su diseño original, lo que reduce la calidad del
software [Mens2004]. El objetivo de este trabajo es brindar un enfoque de refactorización que
garantice una mejora en la modificabilidad del software.
Los atributos de calidad son características que permiten verificar y medir el grado de
satisfacción de los usuarios y/o diseñadores con respecto al sistema de software [ISO9126].
Consideraciones de la lógica de negocio determinan qué cualidades deben ser tenidas en
cuenta en la arquitectura del sistema. Estas cualidades están por encima de la funcionalidad,
que es la declaración básica de las capacidades, los servicios y el comportamiento del sistema
[Bass2003].
La modificabilidad tiene que ver con el costo del cambio [Bass2003]. Cada vez que se desea
introducir un cambio en el sistema se debe tener en cuenta el impacto del mismo y el esfuerzo
que se requiere para realizarlo exitosamente. Estos cambios en el sistema deben poder ser
testeados y desplegados sin afectar el comportamiento observable del sistema. Para
representar posibles cambios en la arquitectura del sistema, se utilizan escenarios de
7
modificabilidad. De esta manera, un arquitecto puede especificar en un lenguaje común a todos
los desarrolladores, las tareas a realizar y los resultados a obtener.
El objetivo en este trabajo es ir más allá de esas pequeñas mejoras que a diario realizan los
programadores para pensar en la refactorización como un proceso enmarcado en el desarrollo
de software que permita mejorar la calidad del sistema a partir de un incremento de su
modificabilidad. Esto significa que las refactorizaciones son realizadas con un enfoque
ordenado, guiado por objetivos concretos, que lleva a modificaciones definidas y validadas, con
el testeo de las áreas de código afectadas y el análisis de los resultados.
• Etapas de Análisis: al comienzo del proceso es necesario el análisis del código del
sistema para detectar errores y síntomas de problemas asociados al desarrollo y las
malas prácticas de diseño.
8
En la Figura 1.1 se puede ver un resumen del proceso definido en este trabajo.
9
ha visto estancado por la erosión del diseño original y la falta de documentación durante el
proceso de desarrollo.
En el capítulo 2 se introducen los conceptos tomados como referencia para este trabajo. Se
sientan las bases de cómo evolucionan los sistemas de software, cuáles son las implicancias
de realizar un refactoring, el análisis a través de code smells y la especificación de los
escenarios de calidad.
10
2. Marco teórico
En este capítulo se introducen los conceptos básicos de la evolución de sistemas de
software y los problemas que esto trae con el paso del tiempo y la introducción de nuevos
features. Durante el desarrollo del sistema se suelen realizar cambios en la arquitectura que no
van en concordancia con su idea original. De esta manera, se cae en un deterioro de la calidad
a partir de la pérdida de modificabilidad, mantenibilidad, flexibilidad y testeabilidad, entre otros
atributos. La implementación de refactorings aparece como una herramienta de desarrollo que
puede ayudar a mejorar la calidad sin alterar el comportamiento observable del sistema. A partir
de la experiencia de expertos en la materia, se introducen las principales características de los
refactorings, sus ventajas y desventajas, el impacto en el código y el diseño del sistema y la
adopción del refactoring como una práctica regular en el proceso de desarrollo.
Para refactorizar, es importante apoyarse en métricas que permitan evaluar la calidad del
diseño. En este trabajo las principales métricas utilizadas son los code smells: un indicador de
síntomas de malas prácticas a nivel de código y diseño. Se presenta una clasificación y las
características de los principales code smells.
Todos estos conceptos son utilizados durante el desarrollo de este trabajo para definir y
validar un proceso de refactoring guiado por escenarios de calidad y code smells.
11
Los requerimientos evolucionan y es por eso que el sistema tiene que hacer lo propio para
no quedar obsoleto [Lehman2001]. Lehman en tres de sus leyes hace referencia a esta
problemática relacionada con el mantenimiento del software:
Para que los sistemas de software evolucionen en el tiempo sin que su calidad sufra un
deterioro se deben realizar cambios progresivos y continuos para que el mismo se adapte a los
nuevos requerimientos y al paso del tiempo. Si las sucesivas modificaciones no son efectuadas
correctamente, el código se vuelve difícil de mantener, se pueden introducir nuevos errores y se
vuelve más difícil adaptar el sistema a los nuevos requerimientos.
Dados los tiempos de desarrollo actuales es importante tener un análisis de cuáles son las
áreas del sistema que deben mejorar su calidad y cuales están estables o no necesitan
cambios según los objetivos a futuro del sistema. Esto permitirá priorizar los cambios a realizar
y lograr una planificación más certera de los tiempos que demandaran estos cambios.
2.2. Refactoring
Refactoring es el proceso de cambio de un sistema de software de manera tal que no altera
el comportamiento externo del código pero mejora su estructura interna. Es una manera
disciplinada de limpiar código que minimiza las posibilidades de introducir errores. En esencia
cuando se refactoriza se está mejorando el diseño del código después de que ha sido escrito
[Fowler1999].
12
2.2.1. Beneficios de refactorizar
Al refactorizar un sistema no se pueden solucionar todos los problemas del mismo. Sin
embargo, es una herramienta valiosa, si se aplica regularmente. El refactoring es una
herramienta que puede, y debe, ser utilizada para diversos fines. Las motivaciones para
realizar un refactoring pueden ser varias. A continuación se discuten algunas de ellas.
A la hora de programar, es una mala práctica que no se piense en que otro programador o el
mismo en un futuro tenga que modificar el código. Este es un problema que impacta cuando un
programador necesita una semana para hacer un cambio que habría tomado sólo una hora si el
código fuera más entendible.
El refactoring ayuda a hacer el código más legible. Cuando existe código que funciona, pero
no está muy bien estructurado, un poco de tiempo dedicado a refactorizar puede hacer que sea
más entendible y rápido de modificar.
13
2.2.2. Momentos para refactorizar
Se recomienda refactorizar todo el tiempo en pequeñas ráfagas. El desarrollador no decide
refactorizar, refactoriza porque quiere hacer algo más, y refactorizando ayuda a preparar el
sistema para el cambio. A continuación se discuten algunas guías de cuándo es recomendable
refactorizar.
14
2.2.2.3. Al revisar el código
Las revisiones de código ayudan a difundir el conocimiento a través de un equipo de
desarrollo. Los desarrolladores más experimentados pasan conocimiento a otros con menos
experiencia. También son muy importantes para la escritura de código claro. El código puede
parecer claro para el desarrollador que lo escribió, pero no para el resto del equipo. Las
revisiones también dan la oportunidad para que más personas sugieren ideas útiles.
Esta idea de la revisión de código activo es llevada a su límite con la Programación Extrema
(XP) y su práctica de la programación en parejas [Beck2000]. Con esta técnica todo el
desarrollo importante se hace con dos programadores en una sola máquina. En efecto se trata
de una revisión de código continua e integrada en el proceso de desarrollo, y la refactorización
que tiene lugar se integra también.
Muchas personas consideran que el diseño debe ser la pieza clave de un proyecto y la
programación sólo algo mecánico. La analogía en la que se cae con esta afirmación es la del
diseño de un plano de ingeniería y la programación vendría a ser el trabajo de construcción.
Pero el software es muy diferente de una construcción de un edificio, ya que es mucho más
maleable.
15
una solución razonable. A medida que se construye la solución, más se entiende sobre el
problema, puede ser que la mejor solución sea distinta a la que originalmente se pensó. Con
refactorización esto no es un problema, porque ya no es caro hacer los cambios.
2.2.3.3. Flexible
Las soluciones flexibles son complejas. El software resultante es más difícil de mantener, en
general, a pesar de que es más fácil de cambiar la dirección que se tenía en un principio. En
caso de que se quiera adaptar el sistema, se debe entender cómo cambiar el diseño. La
construcción de flexibilidad en todo el sistema lo hace mucho más complejo y costoso de
mantener. El tema en este aspecto es un trade-off entre el costo de mantener un sistema con
una gran flexibilidad y el beneficio si estamos preparados para adaptarnos fácilmente a los
cambios, lo difícil es saber cual es la medida justa.
Con la refactorización se tratan los riesgos del cambio de forma diferente. Todavía se piensa
en los posibles cambios, todavía se consideran soluciones flexibles. Pero en lugar de aplicar
estas soluciones flexibles, se deben realizar las siguientes preguntas, "¿Qué tan difícil va a ser
refactorizar una solución sencilla a una solución flexible?" Si sucede como la mayoría de las
veces, la respuesta es "bastante fácil", entonces se debe implementar la solución simple.
La refactorización puede conducir a diseños más simples sin sacrificar la flexibilidad. Esto
hace que el proceso de diseño más fácil y menos estresante. Se apunta a construir la
funcionalidad más simple con la que se es posible trabajar y refactorizar de ser necesario. En
cuanto al diseño flexible, complejo, la mayoría de las veces no se va a necesitar.
16
2.3.2. Funcionalidad y arquitectura
Los atributos de funcionalidad y los de calidad son ortogonales. Si no fuera así, la elección
de la función dictaría el nivel de seguridad o el rendimiento o la disponibilidad o la facilidad de
uso [Bass2003].
La importancia de los atributos de calidad debe ser considerada tanto en el diseño como en
la implementación, aunque ningún atributo de calidad es totalmente dependiente de uno ni otro
aspecto del sistema.
La funcionalidad es la capacidad del sistema para hacer el trabajo para el que fue diseñado.
Una tarea requiere que muchos o la mayoría de los elementos del sistema trabajen de manera
coordinada para completar el trabajo, tal como para construir una casa, albañiles, electricistas,
plomeros, pintores y carpinteros todos trabajan juntos cooperativamente. Por lo tanto, si a los
elementos no se le ha asignado las responsabilidades correctas o no han sido dotados con las
cualidades para poder coordinarse con otros (de modo que, por ejemplo, no saben cuándo es
el momento para que comienzan su parte de la tarea), el sistema será incapaz de ofrecer la
funcionalidad requerida.
17
El atributo de calidad que guiará este proceso de refactoring es la modificabilidad, ya que el
objetivo principal es atacar este atributo. De esta forma, con la mejora de la modificabilidad,
será más fácil y rápido adaptar nuevas funcionalidades en el futuro.
Siguiendo el ejemplo descrito en la figura 2.1, se puede notar que el refactoring impactó de
forma positiva en la modificabilidad del sistema. Es decir, al momento de implementar la
construcción de grafos a partir de otra fuente de información (Chats), no se requiere cambiar el
diseño.
2.3.3. Modificabilidad
La modificabilidad tiene que ver con el costo del cambio. Esto trae a colación dos
preocupaciones [Bass2003]:
18
Una vez que se ha determinado un cambio, la nueva aplicación debe ser diseñada,
implementada, probada y desplegada. Todas estas acciones toman tiempo y dinero, los cuales
se puede medir.
En la tabla 2.1 se hace una descripción detallada de cada una de las partes de un escenario
y los posibles valores que pueden adquirir.
Fuente del Esta parte especifica quién hace los cambios: el desarrollador, Usuario final,
Estímulo administrador de sistemas o un usuario final. Claramente, debe desarrollador,
haber mecanismos para permitir que el administrador del sistema o administrador del
el usuario final puedan modificar un sistema, pero esto es una sistema
ocurrencia común.
Estímulo Esta parte específica los cambios a realizar. Un cambio puede ser la Desea agregar /
adición de una función, la modificación de una función existente, o la eliminar / modificar
eliminación de una función. También se puede hacer para las una funcionalidad,
cualidades del sistema por lo que es más sensible, aumentando su atributo de calidad,
disponibilidad, y así sucesivamente. La capacidad del sistema capacidad.
también puede cambiar. Aumentar el número de usuarios
simultáneos es un requisito frecuente.
Ambiente Especifica cuándo se puede realizar el cambio: en tiempo de diseño, Tiempo de ejecución,
en tiempo de compilación, en tiempo de construcción, e tiempo de compilación, build,
inicio, o en tiempo de ejecución. diseño,
implementación.
Respuesta Quien hace el cambio debe entender cómo hacerlo, y luego hacerlo, Localiza lugares de
19
probarlo y desplegarlo. la arquitectura a ser
modificados, realiza
modificaciones sin
afectar otras
funcionalidades,
testea
modificaciones,
despliega
modificaciones.
Medida de Todas las posibles respuestas toman tiempo y cuestan dinero, Costo en términos de
la tiempo y costo son las medidas más deseables. El tiempo no número de
Respuesta siempre es posible de predecir, sin embargo, se pueden pensar otras elementos afectados,
medidas que se utilizan con frecuencia, como la magnitud del cambio esfuerzo, dinero;
(número de módulos afectados). En relación con la modificabilidad, medida en que esto
la respuesta también puede medirse a partir de indicadores afecta a otras
estadísticos del código y el diseño o bien partir del criterio de un funciones o atributos
experto. de calidad.
20
Porción del Valores
Escenario
Ambiente Implementación.
21
2.3.5.1. Localización de modificaciones
El principal objetivo de este conjunto de tácticas de modificabilidad es minimizar el número
de módulos que necesitarán ser modificados en iteraciones posteriores desarrollo. Las
siguientes tácticas de localización de modificaciones son empleadas durante la fase de diseño
e implementación del proceso de desarrollo [Bass2003].
En esta metáfora, hacer las cosas de la manera rápida y no planeada acarrea una deuda
técnica, que es similar a una deuda financiera. Al igual que una deuda financiera, la deuda
técnica incurre en el pago de intereses, que vienen en la forma de un esfuerzo extra que habrá
que hacer en el futuro desarrollo. Se podrá optar por seguir pagando los intereses, o bien
resolver el problema de fondo, refactorizando el diseño. Aunque es costoso “pagar” (resolver) el
problema de fondo, se gana con pagos reducido de “intereses” en el futuro.
La gestión de la deuda técnica debe reservarse para los casos en los que se ha adoptado
una estrategia de diseño que no es sostenible en el largo plazo, pero se obtiene un beneficio a
corto plazo, tal como lanzar una release. El punto es que los rendimientos de la deuda se vean
pronto, pero la misma tiene que ser pagada tan pronto como sea posible [Fowler2009].
22
cuánto la recompensa por una versión anterior es mayor que los costos de pagarla. Un equipo
que ignora las prácticas de diseño está asumiendo su deuda imprudente sin siquiera darse
cuenta de en cuánto “interés” se está incurriendo.
Pero no siempre podemos adelantarnos y prevenir la deuda, por lo tanto, en estos casos sólo
se puede recurrir a un proceso de refactoring. Si se quiere que un proyecto llegue a buen
puerto, al final siempre se tendrá que pagar la deuda técnica que se ha generado.
Al tener un conocimiento previo del proyecto, se realiza una tormenta de ideas de todas las
deudas del proyecto en que se piensa que han incurrido los programadores y diseñadores que
trabajaron desde el inicio del proyecto. Luego se categorizan cada una de estas deudas según
esfuerzo e impacto. Vale aclarar que se trabaja sobre la deuda técnica conocida, es decir,
cuanto mayor conocimiento del sistema tengan las personas involucradas en la priorización,
mejor será el resultado de la retrospectiva.
En detalle, se prioriza según dos ejes o conceptos: primero el esfuerzo, que es el trabajo
relativo que el equipo tendría que invertir para pagar esta deuda; y segundo el impacto, que es
el efecto directo en la productividad que esta deuda provoca o la importancia que tiene para el
proyecto, resolver el problema.
Se debe pagar la deuda principal para refactorizar nuestro código lo antes posible de no ser
así, siempre se estará pagando sólo los intereses. El no pagar la deuda técnica repercute en la
reducción de la productividad a medida que se agregan nuevos requerimientos al sistema. Se
puede llegar al punto de que no se puede realizar una nueva funcionalidad requerida, ya que, el
sistema no está preparado para ser modificado fácilmente. La pregunta que se hace en este
momento es: ¿Qué parte de la deuda técnica se debe pagar primero?
Para sistematizar este proceso se realiza un diagrama de cuatro cuadrantes dados por la
intersección de los dos criterios en cuestión que se nombran anteriormente. Esto permite
identificar qué parte se puede pagar para tener el mayor retorno (costo / beneficio) y qué parte
no vale la pena pagar ya que implica un alto esfuerzo y baja repercusión. Por eso, el cuadrante
inferior derecho (Bajo esfuerzo / Alto impacto), es el primero que conviene pagar mientras que
la deuda ubicada en el cuadrante superior izquierdo (Alto esfuerzo / Bajo impacto) no vale la
pena ser pagada.
23
Los cuadrantes restantes requieren un mayor análisis, el cuadrante superior derecho (Alto
esfuerzo / Alto impacto) corresponde con la deuda que es compleja de refactorizar pero por la
cual se está pagando un alto interés. Es decir, en la mayoría de los casos son cambios que
involucran una modificación en la arquitectura del sistema, y por lo tanto afectan varios
componentes del sistema. Llegado el momento esta deuda debe ser pagada sin importar el
esfuerzo que se requiere. En contraposición, el cuadrante inferior izquierdo (Bajo esfuerzo/
Bajo impacto), es la parte de la deuda por la que se paga poco interés ya que no causa
demasiado problema en el diseño y más allá de que el esfuerzo por solucionarlo es
relativamente poco, sólo se realizará en caso de que se tenga tiempo disponible.
24
megabytes. Estos números no son más que los valores de algunos parámetros básicos. Para
los sistemas orientados a objetos las más comunes son las líneas de código, el número de
clases, el número de paquetes o subsistemas, el número de operaciones (métodos), etc.
Pero estas métricas por sí solas de forma aislada no permiten hacer una caracterización
acertada del sistema [Lanza2007]. Es importante entender y tener en claro cómo es la
correlación entre las distintas métricas y desde qué punto de referencia se realiza una
comparación para realizar un análisis cualitativo.
Una aplicación, una clase, un método y cualquier otro artefacto en un sistema de software
deben aplicarse de una manera armoniosa, por ejemplo, un clase tiene que implementar un
número apropiado de métodos con un apropiado tamaño, complejidad y funcionalidad. Esta
armonía global está compuesta por tres armonías distintas que afectan a todos los artefactos
de un programa:
Las métricas miden elementos estructurales y, como tales, pueden revelar síntomas ocultos.
Pero siempre habrá una brecha entre los síntomas y la evaluación profunda que un experto en
diseño orientado a objetos puede hacer usando estos síntomas. Por lo tanto es importante
tener en cuenta a las métricas como una herramienta y como con cualquier herramienta
conocer sus ventajas y desventajas.
Fowler remarca que “no hay un conjunto de métricas que sea rival para la intuición humana”
[Fowler1999]. Pero hay que tener en cuenta una desventaja: “la intuición humana no escala con
las dimensiones de los sistemas de la actualidad” [Lanza2007]. Sin embargo, tanto Fowler
como Lanza, coinciden en la utilización de code smells como indicadores de malas prácticas de
programación y en la identificación de oportunidades de refactoring.
25
desarrollo o aumentar el riesgo de errores o fallos en el futuro. Son el resultado de malas
prácticas en la programación: generación de código duplicado, métodos muy largos, métodos
que necesitan demasiados parametros, clases muy grandes, etc.
God Class
En un buen diseño orientado a objetos la inteligencia de un sistema se distribuye
uniformemente entre las clases de más alto nivel. El defecto de diseño God Class se refiere a
las clases que tienden a centralizar la inteligencia del sistema. Una God Class hace demasiado
trabajo por cuenta propia, delega sólo detalles menores a un conjunto de clases triviales
26
utilizando los datos de otras clases. Esto tiene un impacto negativo en la reutilización y la
incomprensibilidad de esa parte del sistema.
Feature Envy
Los objetos son un mecanismo para mantener juntos los datos y las operaciones que
procesan esos datos. La anomalía de diseño Feature Envy se refiere a los métodos que
parecen más interesados en los datos de otras clases que en los de su propia clase. Estos
métodos acceden directamente o a través de métodos de acceso a una gran cantidad de datos
de otras clases. Esto podría ser una señal de que el método está fuera de lugar y que debe ser
trasladado a otra clase. Los datos y las operaciones que modifican o utilizan estos datos deben
estar lo más cerca posible. Esta proximidad entre operaciones y datos puede ayudar a
minimizar el efecto dominó, es decir, un cambio en un método provoca cambios en otros
métodos y así sucesivamente.
Brain Method
A menudo, un método que empieza como un método "normal" pero luego se le añade más y
más funcionalidad hasta que se sale de control, llegando a ser difícil de mantener o entender.
Los Brain Methods tienden a centralizar la funcionalidad de una clase, de la misma manera en
que una God Class centraliza la funcionalidad de todo un subsistema, o incluso a veces un
sistema entero.
Brain Class
Esta anomalía de diseño se da por clases complejas que tienden a acumular una cantidad
excesiva de inteligencia por lo general afectada por varios Brain Methods. Este smell puede
resultar parecido a God Class ya que los dos tienen la tendencia de centralizar la inteligencia
del sistema. Ambos smells hacen referencia a clases complejas pero los problemas son
distintos.
27
Data Class
Son clases contenedoras de datos sin funcionalidad compleja, aunque otras clases
dependen desmedidamente de ellas. La falta de métodos funcionalmente relevantes puede
indicar que los datos y el comportamiento asociado no se mantienen en un solo lugar; esto es
una señal de un diseño no orientado a objetos. Las Data Classes son la manifestación de un
mal encapsulamiento de los datos, y de una baja proximidad entre los datos y la funcionalidad.
Intensive Coupling
Es uno de los casos frecuentes de acoplamiento excesivo en que un método está atado a
muchas operaciones de apenas una clase o un par de clases que guardan poca relación entre
ellas. Se trata de los casos en que simultáneamente se da que la función invoca muchos
métodos y a su vez esos métodos no están demasiado dispersos en muchas clases. Se
entiende que las clases “proveedoras” de métodos guardan poca dispersión o relación entre
ellas cuando pertenecen a la misma jerarquía de clase que la clase que contiene al método
invocante.
28
Dispersive Coupling
El acoplamiento disperso se da cuando una operación está excesivamente ligada a muchas
operaciones en el sistema y, adicionalmente, estos métodos proveedores están dispersos en
muchas clases. Se da una colaboración poco intensa con muchas clases poco relacionadas
entre sí en el sistema.
Este tipo de acoplamiento en las operaciones conduce a efectos dominó no deseados, dado
que un cambio en un método dispersamente acoplado potencialmente conlleva a realizar
cambios en todos los acoplamientos y por lo tanto en todas las clases de las que depende.
Shotgun Surgery
Esta disarmonía de colaboración tiene que ver con las dependencias “entrantes” de un
método, es decir, cuando un método es invocado por muchos otros que a su vez están
dispersos en el sistema. El principal problema que evidencia este smell es que si un cambio
ocurre en el método, es probable que haya que modificar o al menos revisar todos los métodos
que lo invocan desde distintos contextos del sistema. Este síntoma guarda tanta relación con la
intensidad de la colaboración entre entidades como con su dispersión.
2.5. Testing
A la hora de refactorizar es una precondición esencial tener casos de test sólidos. Más allá
que se tenga una herramienta de automatización de refactorings, igual se necesitan tests
[Fowler1999]. Escribir buenos casos de test agiliza la programación en general se esté o no
haciendo refactoring.
Si analizamos en qué gastan su tiempo los programadores, veremos que escribir código es
una pequeña fracción. Un tiempo se pasa descifrando qué es los que se tiene que hacer, otro
tiempo diseñando la solución, pero más tiempo se pasa depurando. Arreglar un bug es una
tarea sencilla, encontrar el bug es la tarea difícil. Todos los programadores han pasado largas
horas para encontrar un error alguna vez. Y existe la posibilidad de, al corregir el error, generar
otro que se descubrirá más tarde y se tenga que pasar de vuelta por el mismo problema.
De esta forma, se ve la necesidad de que cada clase pueda ser testeada cada vez que un
cambio en el sistema es realizado. El programador debe asegúrese de que todos los test son
29
totalmente automáticos, es decir, no debe ver la salida de cada uno de los test y verificar que
esta salida se encuentre en entre los resultados esperados. Los test deben verificar su
resultado e indicar si funciona bien o hay alguna falla en la clase. Se les deben pasar los
parámetros de entrada y chequear su salida para que arrojen los datos esperados.
Ejecutar los test cada vez que se compila el proyecto reflejará una mejora de la
productividad, ya que, en caso de que falle un test ya se sabe que el error está en el trabajo
que se ha hecho desde la última vez que se corrieron los tests. Debido a que el código está
fresco en la mente del programador y es una pequeña porción, el error es más fácil de
encontrar. Errores que podían tardar una hora o más para encontrarlos ahora toman un par de
minutos a lo sumo. Teniendo en cuenta la frecuencia en que se ejecutan, un conjunto de test es
un potente detector de errores que minimiza el tiempo que se tarda en encontrar un error.
Uno de los momentos más útiles para escribir casos de test es antes de empezar la
programación. Cuando se tenga que añadir una característica al sistema se puede comenzar
escribiendo el test. Al escribir antes el test se está pensando en qué resultado se espera
independientemente de cómo se implementa la nueva función. Escribir el test se concentra en
la interfaz en lugar de la aplicación. También significa que el programador tiene un punto claro
en el que termina de codificar: cuando el test funciona [Beck2000].
En el ejemplo de refactoring de la figura 2.1, un posible caso de test podría ser que dada una
misma fuente de información de correos electrónicos, ejecutar el método buildMailGraph de la
clase GraphBuilder y medir que la cantidad de arcos, vértices y las propiedades asociadas a los
mismos sean los esperados. Luego de realizado el refactoring, se vuelve a ejecutar el test para
comprobar que los resultados son los esperados. Este test validará que el cambio se ha
realizado exitosamente sin afectar el funcionamiento observable del sistema.
En síntesis, refactorizar una aplicación requiere testeo, si desea refactorizar se debe escribir
los tests. Aunque no se pueda hacer una cobertura del 100% del sistema, una pequeña
cantidad puede tener grandes beneficios. Para hacer más fácil este trabajo se pueden utilizar
frameworks especializados en automatización de testing, según la tecnología que se esté
utilizando para el sistema.
2.6. Resumen
En este capítulo se han introducido los conceptos que dan sustento a este trabajo. Teniendo
en cuenta cómo es la evolución de los sistema de software y los problemas que la adición de
nuevas funcionalidades pueden causar en la calidad con el paso del tiempo. Se encuentra en el
refactoring una herramienta útil para mantener y mejorar la modificabilidad del sistema,
preparándolo para que la adición de nuevas funcionalidades se haga de forma más rápida y
sencilla.
Estos refactorings, que son necesarios en el sistema, deben poder ser documentados y
especificados en un lenguaje común que puedan entender tanto el arquitecto que los especifica
como los programadores que los implementan. Los escenarios de modificabilidad dan soporte
para esta tarea. Teniendo en cuenta los cambios que el sistema requiere, es importante hacer
una valoración de la deuda técnica para poder planificar y priorizar los refactorings a realizar.
30
Para realizar los diagnósticos de calidad del sistema, para medir su evolución y el impacto de
las tareas de refactorización, es conveniente combinar las métricas con el criterio de los
desarrolladores. Es decir, complementar el análisis objetivo que puede realizarse con los
indicadores cuantitativos, con el análisis subjetivo que sólo pueden aportar los expertos. La
detección de automática de code smells resulta útil para detectar anomalías de diseño e
implementación. Estos indicadores, dan soporte a los desarrolladores para identificar
oportunidades de refactoring y evaluar la calidad del sistema.
31
3. Trabajos Relacionados
En esta sección se describe el estado del arte relacionado a la refactorización de sistemas
en la actualidad. Con el objetivo de presentar los distintos enfoques analizados, los mismos se
explicarán en función de los aportes que realizan en las temáticas que más interesan al
desarrollo de este trabajo final.
Algunos de estos trabajos brindan indicios de problemas que resultan útiles para guiar y
enfocar el proceso de refactoring sobre los sistemas. Algunos se basan exclusivamente en la
identificación de oportunidades de refactorización y otros evalúan las mejoras de calidad
obtenidas a partir de la implementación de distintos tipos de refactoring.
Este capítulo se organiza en cuatro secciones que permiten revisar distintas experiencias en
materia de refactorización de sistemas de software. Las mismas son: aspectos generales del
refactoring de software, enfoques de refactoring, impacto de los refactorings en la calidad del
software y refactoring guiados por code smells.
32
del historial de versiones, revelando que la definición de refactorización en la práctica no se
limita a una definición rigurosa de las transformaciones de código y que los desarrolladores
perciben que la refactorización implica costos y riesgos considerables [Kim2012]. Las figuras
3.1 y 3.2 muestran los resultados de las encuestas a los desarrolladores respecto a los riesgos
y los beneficios de refactorizar respectivamente.
Figura 3.2. Varios tipos de beneficios de refactorizar experimentados por los desarrolladores [Kim2012].
33
resultados positivos, aunque para esto no se utilizaron herramientas de análisis automáticas ni
de apoyo para refactorizaciones semi-automáticas.
34
Ciertas acciones de refactorización como Extract Class tienden a crear más clases, lo que
aumenta la profundidad de la herencia del árbol, lo que aumenta la complejidad general. Otras
acciones de refactorización, tales como Extract Method, pueden ser utilizados para reducir
líneas totales de código y reducir la duplicación en el código, lo que disminuye el tamaño de la
aplicación y aumenta la mantenibilidad, con lo que aparentemente aumentar la calidad global
de software para la aplicación. Sabiendo esto, faltaría aún investigar en profundidad la relación
entre ambos tipos de métricas, estudiar qué técnicas, enfoques y herramientas de
refactorización se aplican mejor para optimizar un tipo u otro de métricas.
● Implementar los refactorings. Etapa que en general se divide en 2 fases: verificar las
pre condiciones apropiadas para que el refactoring no afecte la funcionalidad del
sistema y efectivamente aplicar las refactorizaciones.
En general, los entornos de desarrollo modernos proveen soporte para la segunda fase del
paso 3, pero no para el resto. El soporte automatizado puede facilitar la identificación de
oportunidades de refactorización y más específicamente sugerir qué tipo de refactoring se debe
implementar. Si bien existen herramientas para la detección de malos síntomas en un sistema,
aún es necesario el análisis del desarrollador para discriminar y ponderar los problemas
detectados, que pueden ser una gran cantidad, distribuidos en todo el sistema.
En este trabajo final se tomó en cuenta esta serie de pasos para definir un proceso de
refactorización basado en el análisis objetivo de las métricas y en el análisis subjetivo realizado
por los desarrolladores.
35
estructuras estáticas de análisis que permiten al desarrollador apreciar de manera gráfica por
ejemplo el grado de acoplamiento o cohesión en un determinado conjunto de artefactos
[Simon2001]. En la figura 3.3 se puede apreciar un ejemplo del uso de una herramienta de
visualización que sugiere una refactorización. Si bien este tipo de herramientas puede ser útil
para la detección de oportunidades de refactoring, es necesario incluirlas en un proceso paso a
paso que realice un análisis del sistema, no sólo a nivel de código, sino de su arquitectura.
Figura 3.2. Herramienta VRML-World. Los métodos se representan con esferas y los atributos con cubos. Cada
clase tiene asignado un color. La herramienta sugiere una refactorización: un método de la clase “verde” debe
movido a la clase “azul” [Simon2001].
36
Tanto el proceso como la herramienta de refactorización semiautomática tienen como
objetivo la reutilización de features existentes del sistema para modificarlos y utilizarlos para
componer nuevos features. Sin embargo, nunca se hace hincapié en la calidad del sistema ni
en la deteccion y correccion en el proceso de posibles problemas de diseño.
37
orientados a objetos. El refactoring es a menudo utilizado como parte del desarrollo del
software, como es el caso de la metodología Extreme Programming (XP), donde el refactoring
es una parte central del ciclo de desarrollo.
38
Se ha desarrollado un método para el análisis de modificabilidad de arquitecturas de
software basado en escenarios de calidad [Bengtsson2000]. Este enfoque consta de 5 pasos:
(1) definir el objetivo, (2) describir la arquitectura, (3) elicitar los escenarios, (4) evaluar los
escenarios y (5) interpretar los resultados. El método desarrollado permite trabajar en tres tipos
de objetivos: predicción de mantenimiento, evaluación de riesgos y selección de arquitecturas
de software. Según el objetivo, se combinan distintas técnicas en los siguientes pasos del
proceso. Si bien, el método parece robusto para analizar la arquitectura de un software, no ha
sido probado en casos de estudio concretos. El método tampoco se aplica para analizar la
estructura interna del software, es decir, su diseño e implementación.
39
Naturalmente, muchas de estas métricas, sirven también para medir los resultados luego de
aplicar las refactorizaciones al sistema. En este trabajo final se propone utilizar los code smells
con estos propósitos. A continuación se revisa una serie de experiencias basadas en code
smells.
El aporte de este trabajo es identificar en qué áreas de código están localizados la mayor
cantidad de code smells. Para los usuarios del caso de estudio esto permite usar la
herramienta como un monitor para ver cómo varía la calidad del sistema a medida que va
evolucionando. Sin embargo, la herramienta y enfoque no hacen hincapié en cómo resolver los
problemas de modificabilidad del sistema si no que sólo brinda soporte para el análisis y la
detección.
40
smells de código [Garcia2009]. Estos son Connector Envy, Scattered Functionality, Ambiguous
Interfaces, Extraneous Connector. Una vez detectado un architectural smell, se puede evaluar
el impacto de calidad en el sistema para pensar en refactorizaciones a nivel arquitectónico. Si
bien es sabido que los problemas estructurales son los que más limitan la evolución del
sistema, realizar cambios en la arquitectura del sistema tiene un riesgo muy elevado. Por eso,
es conveniente identificar esos problemas pero dividirlos en problemas más pequeños, de
modo tal de buscar soluciones que mitiguen riesgos y sumadas, permitan una mejora a nivel
global.
41
Figura 3.1. Orden de resolución de code smells recomendada [Liu2009] .
2. Debe ser adaptado según sea necesario para los proyectos que no se ajustan a los
límites de pequeños equipos recomendado por los fundadores de esta metodología ágil.
42
En el trabajo puntualiza la experiencia en un proyecto a gran escala aplicando la metodología
XP, haciendo evolucionar el diseño de software con refactorizaciones sucesivas hasta obtener
la calidad requerida, teniendo en cuenta el análisis de code smells en el código para detectar
posibles desviaciones o malas prácticas en el desarrollo del sistema. Sin embargo, no define en
sí un proceso generalizado para realizar las refactorizaciones, sino específicamente cómo
ajustar la metodología XP al contexto de un determinado proyecto de software.
3.5. Resumen
En resumen, en este capítulo se realizó un repaso general de las distintas tácticas de
refactorización, el uso de herramientas de soporte y distintas formas de medir las mejoras
obtenidas por la refactorización de sistemas de software.
43
4. Proceso de Refactorización
La refactorización de sistemas informáticos, tiene el objetivo de mejorar la estructura interna
del software, sin alterar su funcionalidad [Fowler1999]. Bajo esta definición, cualquier cambio,
por pequeño sea, si representa una mejora y no afecta la funcionalidad del sistema, podría
considerarse una “refactorización”. Esto sucede a diario y de manera implícita y natural por
parte de los desarrolladores de un proyecto de software.
Se pretende ir más allá de esas pequeñas mejoras que a diario realizan los programadores
para pensar en la refactorización como un proceso enmarcado en el desarrollo de software que
permite mejorar la calidad del sistema a partir de un incremento de su modificabilidad. Esto
significa que las refactorizaciones se enmarcan en un proceso ordenado, con objetivos
concretos, modificaciones definidas y validadas, testeo de funcionalidades afectadas y análisis
de resultados.
El propósito de este proceso de refactoring es hacer el sistema más fácil de entender, ya que
un software fácil de entender es un software fácil de modificar [Fowler1999]. Por lo tanto este
proceso tendrá como resultado una mejora de la calidad del software desde el punto de vista
de la modificabilidad.
44
A continuación se describen los principales aspectos del proceso utilizados en este trabajo
final.
En la figura 3.1 se detalla el flujo del proceso planteado y los distintos grupos de etapas
propuestas para la refactorización. El proceso consta de 6 etapas:
45
A continuación, se detallarán en profundidad cada una de las etapas y sus implicancias.
46
4.1.1.1. Etapa 1: Análisis e identificación del problema ¿Qué
refactorizar?
En esta etapa se deben identificar las secciones de código en las cuales el proyecto es poco
modificable, es decir, cuáles son las clases y paquetes que no permiten agregar una nueva
funcionalidad de forma sencilla. Este aspecto está ligado directamente con los cambios que se
deseen realizar, ya que no se trata de mejorar este atributo de calidad en cualquier paquete o
clase del sistema, sino en aquellas áreas directamente afectadas por los requerimientos
funcionales que se proponen desarrollar en un futuro. Por eso, es necesario un reconocimiento
del estado actual del proyecto pero también tener una noción de los próximos pasos en su
desarrollo. A continuación se presentan algunos lineamientos que sirven como guía para lograr
una refactorización eficiente:
• Documentación del proyecto. Para comprender mejor los procesos y flujos de datos
del sistema, es preciso consultar la documentación del proyecto, en especial lo que se
47
refiere a aquellas áreas del código potencialmente afectadas por las nuevas
funcionalidades que se busca desarrollar. Si bien muchos proyectos de desarrollo no
cuentan con documentación actualizada, se pueden generar nuevos documentos que
ayuden a entender cómo interactúan distintos componentes del software. Para esto, son
de gran utilidad los diagramas estandarizados de UML tales como diagrama de clases,
diagrama de interacción y diagrama de actividades, entre otros. La confección de
diagramas tipo UML permite documentar en un lenguaje conocido por los
desarrolladores y a su vez facilita la detección de debilidades en el diseño, por ejemplo
a través de un diagrama de clases. El objetivo no es documentar toda la arquitectura de
manera formal, sino valerse de una descripción más clara del problema concreto que se
desea abordar. Significa que, en cada caso de refactorización, habrá que evaluar si es
necesario generar un nuevo documento, de qué tipo y en qué área específica del
sistema.
• Detección de code smells. Para esta etapa del proceso también se ha optado por
utilizar los code smells como síntomas de malas prácticas que podrían impactar en el
diseño y potencialmente en la modificabilidad del sistema. Es así que una vez
identificadas las “áreas conflictivas” del código se propone observar la presencia de
smells como guía para proponer refactorizaciones de modificabilidad. La detección de
smells puede realizarse a través de herramientas automatizadas. Por ejemplo, para este
trabajo se usa JSpIRIT [JSpIRIT], un plugin de Eclipse que permite detectar los smells y
genera un rankeo de los mismos a partir de distintos criterios como escenarios de
calidad y/o la asignación de pesos a cada tipo de smell. Dada la gran cantidad de smells
que pueden encontrarse en un proyecto de software, el ranking de los mismos es
importante para seleccionar los síntomas que deben abordarse con mayor prioridad.
Como se ha visto, los code smells no son necesariamente errores, sino síntomas de
malas prácticas de implementación que pueden llevar a la detección de falencias en el
diseño del sistema. Por lo tanto, es necesario el estudio de cada caso en su contexto y
en los objetivos generales del proceso de refactorización.
48
El análisis de problemas en el sistema debe ser metódico, ordenado y fundamentado. En
general es realizado por los desarrolladores, que son quienes mejor conocen su arquitectura e
implementación. Los miembros del equipo conocen en profundidad el software y a la hora de
incorporar cambios sabrán por dónde empezar y qué restricciones existen para que el sistema
se adapte a las nuevas funcionalidades que se desean incorporar. Sin embargo, la intuición
humana no es escalable a sistemas de grandes dimensiones [Lanza2007] y por lo tanto para
encontrar y mejorar anomalías de diseño es necesario emplear estrategias que incluyan el uso
de herramientas que automaticen la detección de síntomas. Es así que se debe combinar el
análisis objetivo obtenido a través de indicadores estadísticos y herramientas de detección con
el análisis subjetivo que proporcionan las personas abocadas al desarrollo. Al finalizar esta
etapa se deberá contar con una serie de artefactos -clases y paquetes- ligados a una
determinada funcionalidad o sección del sistema que presente síntomas de problemas de
diseño y/o implementación. Así, en la próxima etapa se podrá trabajar en mejoras para esas
irregularidades.
Una vez identificados los problemas más significativos en el sistema acorde a los objetivos,
el equipo de trabajo puede comenzar a pensar cómo abordar cada conflicto proponiendo
cambios que devengan en mejoras de calidad. El alcance de cada solución dependerá
directamente de la dimensión del problema detectado y por lo tanto se podrán sugerir cambios
tanto a nivel de diseño como de implementación. Las actividades a realizar en esta etapa son:
49
• Especificar Escenarios de Modificabilidad. Para especificar cada uno de estos
pequeños cambios para resolver un problema complejo se utilizan escenarios de
modificabilidad. Con este artefacto, se documenta de manera estandarizada el objetivo
de las tareas que tendrán que realizar los desarrolladores. Para ello, se deberán
organizar las tareas a realizar en distintos escenarios de modificabilidad definiendo para
cada uno: fuente del estímulo, estímulo, artefacto, ambiente, respuesta y medida de la
respuesta.
Existen múltiples métodos para realizar una buena ponderación de requerimientos, aunque la
definición de este proceso de refactorización no tiene por objetivo encontrar el mejor de ellos
sino más bien proponer ciertos criterios de priorización que conduzcan a un rankeo no aleatorio
y de esta manera mejorar la calidad del proceso de refactorización. Al instanciar el proceso en
un proyecto se podrá optar por otras métodos si así se considera.
50
• Nivel de Impacto. Esta variable se refiere al nivel de avance que implica la realización
de un escenario. O desde una perspectiva negativa, el nivel de “dolor” o riesgo que
representa ese problema del sistema que se busca solucionar. Naturalmente, tendrán
una prioridad más alta aquellos escenarios de modificabilidad que tengan mayor nivel
de impacto. La valoración de impacto que se le da a un escenario puede variar de
acuerdo al momento en que se encuentra el desarrollo o bien entre los distintos
miembros del equipo de desarrollo ya que cada uno puede tener distintas perspectivas.
Para facilitar el criterio, es importante tener siempre presentes los objetivos principales
por los cuales se está llevando a cabo el proceso de refactorización.
• Nivel de Esfuerzo. La otra variable a considerar es el esfuerzo, es decir, la cantidad de
recursos que se estima necesaria para llevar adelante la tarea. Generalmente es
medida en horas/hombre de acuerdo al tiempo que requiere para ser implementada.
Este factor depende directamente de las capacidades técnicas de los desarrolladores,
de la comprensión del problema y del nivel de conocimiento que tengan del sistema.
• Ubicación en cuadrantes. La definición de los criterios mencionados es una
estimación que surge del equipo de desarrolladores. Es difícil asignarle a cada
escenario un valor exacto de impacto y esfuerzo, pero lo importante es realizar una
aproximación a partir del análisis objetivo y subjetivo realizado. Se propone realizar un
cuadro de doble entrada con dos valores (alta y baja) para cada criterio, para luego
ubicar cada uno de los escenarios en uno de los cuatro cuadrantes resultantes. De esta
manera, para cada escenario se puede apreciar fácilmente la relación entre impacto y
esfuerzo que se le atribuye:
○ Bajo impacto / Alto esfuerzo: Representa el peor cuadrante, los casos con
menor retorno. No es recomendable priorizar escenarios que requieren mucha
dedicación a cambio de pocas mejoras en el sistema o alto riesgo.
○ Alto impacto / Bajo esfuerzo: El mejor caso, los escenarios prioritarios ya que
con poco esfuerzo se logra un impacto relativamente alto en el sistema.
○ Alto impacto / Alto esfuerzo y Bajo impacto / Bajo esfuerzo: Los casos
intermedios. Habrá que evaluar en cada caso el nivel de prioridad que se le
atribuye.
51
Una vez definidos los escenarios de modificabilidad y ponderados en el backlog, se puede
pasar a la etapa de implementación de cambios en el sistema. Esta tarea puede ser llevada a
cabo por los responsables del proceso de refactorización o bien por cualquier miembro del
equipo de desarrollo del proyecto. En la figura 3.6 se puede ver el diagrama de flujo de las
actividades de esta etapa. A continuación se describe cada una:
52
importante en todos los casos es garantizar la “medida de la respuesta” ya que de esta
manera el escenario se considerará completado.
• Ejecutar casos de test. Una vez realizados los cambios en el sistema, se deben
ejecutar los casos de test correspondientes para comprobar que no se han introducido
errores. En caso de que algún caso de test no se ejecute con éxito, se deberá volver a
la implementación del escenario para corregir el error introducido, como se ven en la
figura 3.6. En este tipo de situaciones es donde el refactoring en pequeños pasos brinda
sus frutos ya que, al no modificar una gran cantidad de clases y realizarse en un tiempo
mucho menor, el error será más fácil de encontrar.
Se espera que con cada implementación realizada se mejore en mayor o menor medida la
modificabilidad del software, ya que hay escenarios que impactan significativamente en las
métricas y otros que su valoración es más bien subjetiva. Por esta razón, la correcta realización
de cada escenario se determinará verificando la medida de su respuesta, la cual guarda
relación directa con la modificabilidad de todo el sistema o de una sección del mismo. Las
actividades a realizar en esta etapa son:
53
que es necesario revisar o modificar para introducir un cambio y/o métricas relacionadas
con la modificabilidad como por ejemplo: abstractness, average cyclomatic complexity,
average number of parameters, efferent couplings, lines of code, weighted methods,
entre otros.
• Análisis subjetivo y valoración del grupo de desarrollo. En algunos casos es
necesario el análisis de los responsables del proceso para determinar si el escenario
está resuelto o no. Es decir, en esta etapa también será válido el análisis subjetivo de
los desarrolladores que conocen la implementación del software. Por ejemplo, luego de
la refactorización de un artefacto, se podrá evaluar el nivel de esfuerzo requerido para
comprender y modificar dicho artefacto. Este tipo de observaciones solo pueden
realizarse mediante el juicio humano de los desarrolladores del sistema o bien el de un
evaluador de calidad externo.
• Validación del escenario. La evaluación de resultados será de carácter cuantitativa,
cualitativa o bien una combinación de las dos a partir de la complementariedad de
distintas herramientas de análisis. En cualquier caso, el escenario será validado en
tanto se alcance la “medida de la respuesta” especificada en su definición formal.
Finalmente, una vez determinada la resolución del escenario, se deberá tomar el siguiente
escenario del backlog (etapa 4). En caso de no quedar más escenarios, se procederá a la
última etapa de la iteración.
En principio, esta decisión debería ser guiada por las mejoras de calidad en cuanto a la
modificabilidad. Si el sistema se considera lo suficientemente modificable para el desarrollo de
las próximas funcionalidades del proyecto o bien si se observa una notable disminución de la
deuda técnica, entonces el proceso se podrá considerar “terminado”.
En caso que las métricas no revelen una mejora considerable, se podrá optar por una nueva
iteración del proceso. Seguramente, en etapas anteriores se hayan detectado problemas o
síntomas que evidencian la necesidad aplicar refactorizaciones en ciertas secciones del
sistema. En general, los programadores suelen encontrar posibles mejoras para el sistema que
se está desarrollando, de hecho, algunos autores sugieren realizar las refactorizaciones de
manera constante, revisando y mejorando el código a la vez que se lo implementa, analiza y
mantiene [Beck2000], [Liu2012], [MurphyHill2008].
54
5. Caso de Estudio
Todo proceso de refactorización se realiza sobre el código de un sistema de software y en el
marco de su proyecto de desarrollo. A la hora de pensar en un refactoring, es importante
conocer el código, el diseño del sistema y su arquitectura. Es decir, el “éxito” o “fracaso” del
proceso de refactorización depende en gran medida del grado de conocimiento que se tenga
de la estructura del proyecto. En caso de no existir este conocimiento, los responsables del
refactoring deberían comenzar por estudiar el código, su diseño, sus fortalezas y sus
debilidades.
55
5.2. Definición del sistema
Si bien no existe seguimiento ni documentación del código del software desde su creación,
se pueden identificar dos grandes “versiones” del mismo. Una primera versión con toda su
funcionalidad principal, sin versionado de código ni documentación sistematizada más allá de
las personas involucradas en su desarrollo. Luego, en el marco de la asignatura “Diseño de
Sistemas de Software”, los autores del presente trabajo accedieron a SocialGraph,
incorporaron funcionalidad, configuraron el versionado del código y elaboraron un informe que
podría considerarse una primera documentación del proyecto de desarrollo. Se considera esta
última etapa como la segunda versión de SocialGraph. Entre las funcionalidades añadidas en la
última versión se destaca: integrar la descarga de emails desde servidor POP o IMAP,
posibilidad de aplicar distintos tipos de filtros para emails descargados, generar grafo a partir de
mails descargados, integración de archivos adjuntos en emails, persistencia y fusión de emails
extraídos de varias cuentas, extensión de la interfaz gráfica de usuario.
56
Al estudiar la estructura de los paquetes y bibliotecas del proyecto en relación a su
funcionalidad, en principio se puede decir que el sistema no cuenta con un estilo arquitectónico
tipificado, definido. Sin embargo, se pueden identificar a grandes rasgos, como se puede ver en
la figura 5.1, los siguientes componentes y dependencias, con funciones específicas dentro del
sistema:
• Model: Define las clases que hacen al modelo de red social, entre ellas Mail, File
Attachment, Tag y las utilizadas para modelar el grafo AbstractEdge, AbstractVertex,
AttachmentEdge, Attachment, CommunicationEdge, FileVertex, PersonVertex. Puede
notarse que el grafo se compone por dos tipos de vértices, los de personas y los de
archivos y a su vez por dos tipos de arcos, los de comunicación (mensajes entre
personas) y los de adjuntos (archivos entre personas). Esta es una gran característica
del sistema que tendrá un impacto directo en la implementación y escalabilidad del
mismo.
• Profile & Role: La herramienta SocialGraph permite especificar y/o detectar distintos
roles de las personas (vértices) de la organización en base a los tópicos que
frecuentemente involucran a dicha Persona.
• Importer: Provee una interfaz para importar datos de emails desde dumps de bases de
datos. También permite descargar mails directamente de servidores de correo, aunque
este feature no funcionaba correctamente y por eso se ha implementado como parte de
las mejoras al sistema.
57
Otra debilidad de la arquitectura es el grado de acoplamiento entre modelo, vista y
controladores. Es decir, no se puede distinguir estos módulos en la implementación. Esta
característica le quita escalabilidad a la plataforma y prácticamente impediría, por ejemplo,
cambiar el motor gráfico por otro o hacer un cambio en las estructuras del modelo. Al dibujar el
grafo, la vista accede directamente a la clase Graph del modelo e implementa allí la lógica de
procesamiento, filtrado, extracción de tags, etc. Sería deseable realizar algunas optimizaciones,
orientando el diseño a una arquitectura de bandas para ganar independencia entre los
componentes del sistema.
En los diagramas de secuencia de las figuras 5.2, 5.3 y 5.4 se puede ver dos de los procesos
más significativos de la aplicación para los objetivos de este trabajo. Estos son la carga de un
grafo desde los archivos generados en la construcción del mismo y la importación de una
fuente de información para la construcción del grafo. En el figura 5.2 se puede ver la
secuencias de invocaciones y las clases involucradas en la carga de un grafo a partir de
correos electrónicos. El sistema carga de distintos archivos, el esquema del grafo y sus
propiedades. Este diagrama es importante ya que hace visible el acoplamiento del modelo del
grafo a la fuente de información de tipo Mail.
En las figuras 5.3 y 5.4 se aprecian los diagramas de secuencia donde se muestra los
métodos invocados y las clases relacionadas a la importación de datos desde un archivo de
backup de correos electrónicos. En la figura 5.3 se puede ver la primera parte de la
importación, que consta del procesamiento del contenido de los correos y su filtrado previo a la
generación del grafo.
En la figura 5.4 se puede ver el acoplamiento del modelo a la fuente de información de tipo
mail en la construcción del grafo. Para cumplir con el objetivo a futuro de soportar varias
fuentes de información, esta será una de las áreas de código sobre la que habrá que focalizar
el refactoring.
58
Figura 5.3. Diagrama de secuencia de importación de datos en SocialGraph (Parte 1).
59
5.2.3. Funcionalidades de SocialGraph
Como se mencionaba en secciones anteriores, SocialGraph fue desarrollado para extraer
datos de cuentas de correo electrónico, mapearlos y visualizarlos en un grafo para luego aplicar
teoría de grafos y técnicas de análisis de redes sociales.
60
planeado ni enmarcado en una metodología de desarrollo clara, lo cual ha afectado su calidad,
al menos desde el punto de vista de la modificabilidad. Su aplicación en ambientes de
investigación y desarrollo no pierde vigencia y por lo tanto, como todo sistema de software en
uso, constantemente requiere de mantenimiento y ampliación de sus funcionalidades.
En el caso de este proyecto, los usuarios habituales del sistema, se dedican al análisis de las
relaciones y el intercambio de información entre los miembros de una organización. La
credibilidad de los datos utilizados se basa en los correos electrónicos enviados y recibidos por
esas personas. Pero en general, en un grupo de trabajo, existen otras vías de comunicación e
intercambio de información más allá de los correos. Es así que se plantea la necesidad de
desarrollar nuevos módulos de SocialGraph para dar soporte a más “fuentes de información” a
partir de otras herramientas utilizadas, como por ejemplo, documentación, chat, mensajes de
texto, control de versiones de código, seguimiento de tareas, redes sociales online, etc.
Este nuevo requerimiento pone en evidencia la falta de planificación del sistema y su deuda
técnica, ya que SocialGraph fue pensado y desarrollado desde un principio para soportar sólo
correos electrónicos y en eso se basa su diseño arquitectónico. Es por esto que se decide
encarar un proceso de refactorización de la herramienta que resulte en una mejora cualitativa
en términos de modificabilidad y que permita desarrollar la funcionalidad requerida.
Para empezar con este proceso, se realiza una pre-iteración llamada “Iteración 0”, en donde
se busca hacer un análisis general del sistema entendiendo cómo funciona y cuáles son los
componentes del mismo. De esta manera, se logra tener una visión general de cuáles son los
problemas principales de modificabilidad, realizar una priorización de los mismos y planificar de
forma cómo y cuándo atacar cada problema siguiendo el proceso de refactoring.
61
funcionalidades, sabiendo que uno de los requerimientos funcionales proyectados es la
capacidad de generar información a partir de diversas fuentes de datos.
Para esta iteración entran en valor dos variables fundamentales. Primero, cuánta
documentación sobre el sistema se tiene: diagramas UML, especificación de la arquitectura,
etc. Y segundo, el conocimiento que se tenga sobre el código del sistema. Es más fácil para un
desarrollador que trabaja todos los días en el código poder planificar y priorizar qué
refactorizaciones son las más adecuadas. De no ser así se deberá apoyar en mayor medida en
la documentación a la hora de tomar decisiones.
62
funcionalidad que se estima a futuro. Del análisis general del sistema se han identificado cuatro
grandes anomalías de diseño. En esta iteración preliminar se hace una valoración sobre las
mismas pudiendo especificar cuál tiene mayor prioridad. Se utiliza el cuadrante de retrospectiva
de deuda técnica para cumplir con esta tarea.
Como se puede ver en la figura 5.6, las Anomalías 2 y 3 se colocaron en el cuadrante “Alto
esfuerzo / Alto impacto” que corresponde con la deuda que es compleja de refactorizar pero por
la cual se está pagando un alto interés. Son cambios que involucran una modificación en la
arquitectura del sistema y por lo tanto afectan varios componentes, implicando mayor riesgo
ante el cambio.
Si bien la Anomalía 2 está directamente ligada con la modificabilidad del sistema, al evaluarla
en el cuadrante de deuda técnica, se observa que para realizar una mejora significativa, es
necesario un gran inversión de tiempo de desarrollo. Por otra parte, el nivel de incidencia en el
requerimiento de importación de nuevas fuentes de información, principal motivación del
refactoring, es relativamente bajo. Por lo tanto, se decide no abordar este problema en el actual
proceso de refactorización aunque en algún momento se tendrá que pagar la deuda asociada a
esta anomalía.
63
Figura 5.6. Cuadrante de deuda técnica de las anomalías detectadas.
Estas son malas prácticas de desarrollo de software ya que a medida que crece el sistema
se vuelve insostenible testear todo ante cada cambio. Ya que se tendría que ir ejecutando clase
por clase, esperar que se muestre el resultado por pantalla o, en el mejor de los casos, en un
archivo de log, para después verificar si el resultado de la ejecución se corresponde con un
valor esperado. A largo plazo, este esquema es insostenible.
64
Por lo tanto, antes de implementar cada uno de los escenarios es importante chequear la
cobertura de tests. Si no existe un caso de test que le dé cobertura al área de código afectada
por el refactoring, se usará la biblioteca JUnit para automatizar casos de tests unitarios sobre el
código a refactorizar. De esta forma, se validará que el nuevo código cumpla con los
requerimientos anteriores y que no se ha alterado su funcionalidad observable después de una
modificación.
65
tabla 5.1 se muestra la distribución de code smells según su tipo. Se ve que los tipos Feature
Envy y Dispersed Coupling concentrán mas del 80% de los smells.
Importer 43 28,67
GUI 36 24,00
Graph 31 20,67
Model 15 10,00
WordCram 12 8,00
Otros 13 8,66
66
El proyecto cuenta con código de terceros que no se sabe si es necesario ni si fue extendido
o modificado para algún propósito especial. En particular, los frameworks a analizar y
reestructurar son WordCram [WordCram] y GATE [GATE], utilizados para el procesamiento de
textos en la aplicación.
• Mavenizar el proyecto.
67
Porción del Valores
Escenario
Estímulo Reorganizar las dependencias del proyecto usando Maven para tener
mayor facilidad de configuración del ambiente.
Ambiente Implementación.
Dado este problema y el overhead en el tiempo de configuración, se decide usar Maven para
la configuración del proyecto y así tener un mejor manejo de las dependencias del proyecto y,
al mismo tiempo, “limpiar” todas las dependencias que no sean necesarias para el
funcionamiento del proyecto.
Ambiente Implementación.
68
para una nueva terminal del trabajo.
Tabla 5.4. Especificación escenario 1.2: Reestructurar dependencia con aplicación WordCram Proyecto.
Al igual que en los escenarios anteriores, se analizan los componentes del sistema
relacionados a GATE y sus dependencias, pasando estas a Maven si son requeridas.
Ambiente Implementación.
69
5.5.3.1. Nivel de Impacto
El escenario 1.1 tiene mayor prioridad ya que reduce los tiempos para que un desarrollador
configure y se ponga a trabajar en el proyecto con el plus de que optimiza el manejo de
dependencias y el versionado de la aplicación, lo cual implica un alto impacto en la
modificabilidad. Los escenarios 1.2 y 1.3 no tienen un alto impacto para la incorporación de
nuevas fuentes de información, que es la motivación de este trabajo, pero sí permiten estudiar
el sistema en mayor detalle y aplicar la refactorización pertinente hace el sistema más legible,
siguiendo la premisa de que un código fácil de entender es un código fácil de modificar.
70
Priorización de cuadrante de deuda técnica :
71
Jung jung-3d-2.0.1.jar Mavenizar Se agregan a Maven las
j3d-core-1.3.1.jar siguientes dependencias:
<dependency>
<groupId>net.sf.jung</groupId>
<artifactId>jung-3d</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>java3d</groupId>
<artifactId>j3d-core</artifactId>
<version>1.3.1</version>
</dependency>
72
</dependency>
73
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.9.4</version>
</dependency>
74
powerlaws-0.0.1.jar, log4j- Eliminar La dependencia powerlaws-
1.2.16.jar, 0.0.1 afecta la clase
commons-logging-1.1.jar, TestPowerLaw.java en
forms-1.3.0.jar edu.isistan.socialgraph/src/ed
u/isistan/socialgraph/graph/sta
ts. Esta clase no llama a
ninguna otra ni es utilizada. Se
decide borrar la clase y la
dependencia del Java build
Path y no se agrega a las
dependencias maven.
Se decide borrar la
dependencia commons-
math3-3.1.1.jar del Java build
Path y no se agrega a las
dependencias maven.
75
puede que no arroje un error de compilación pero si en el funcionamiento interno de la
biblioteca, alterando de esta forma el comportamiento observable de la aplicación.
Se instala el Jar del proyecto WordCram en el repositorio Maven local. Esto tiene el problema
de que cada vez que alguien descarga el proyecto se tendrá que hacer lo mismo en su
ambiente y la tarea resulta más compleja.
En el pom se especifica cuáles son las dependencias que deben ser previamente instaladas
en el repositorio local o en caso de un futuro en un repositorio Maven propio. Para realizar esto
sólo hay que tener una copia de una carpeta, la cual será subida al servidor git con las librerías
a instalar en el repositorio de Maven local.
76
5.5.4.3.2. Implementar Escenario
Se analiza qué dependencias y clases son utilizadas y requeridas para el funcionamiento del
sistema que tengan relación con la aplicación GATE. En caso de que las dependencias se
requeridas para el normal funcionamiento del sistema deben ser eliminadas del Java Build Path
e incorporadas al archivo de configuración de Maven. En caso contrario, sólo serán eliminadas
del Java Build Path.
<dependency>
<groupId>trove</groupId>
<artifactId>trove</artifactId>
<version>1.0.2</version>
</dependency>
El resto de las dependencias
se incorporan a maven con:
<dependency>
<groupId>uk.ac.gate</groupId>
<artifactId>gate-core</artifactId>
<version>5.2.1</version>
</dependency
77
<groupId>opennlp</groupId>
<artifactId>opennlp-
tools</artifactId>
<version>1.4.3</version>
</dependency>
Tabla 5.7. Refactorizaciones escenario 1.3: reestructurar dependencia con aplicación GATE.
OPENNLP_PROCESSORS_DIR + "SpanishSent.bin.gz";
OPENNLP_PROCESSORS_DIR + "SpanishTok.bin.gz";
OPENNLP_PROCESSORS_DIR + "SpanishPOS.bin.gz";
Se podría quitar todo lo referido a GATE ya que no está siendo utilizado, sin embargo, por
tratarse de una aplicación en constante desarrollo, podría tener que estar preparada para
utilizar GATE en vez de OpenNLPProcessor que actualmente está cumpliendo con la tarea del
procesamiento del texto.
Ante la complejidad que suma tener que instalar todas las dependencias de terceros que no
están en un repositorio de Maven en la nube en un servidor local, se ve que aunque se
resuelve el manejo de versiones de las dependencias se sigue arrastrando el problema de
tener que configurar el entorno cada vez que un nuevo desarrollador tiene que trabajar en el
proyecto.
Por esta razón, se decide agregar todas las dependencias de terceros que no están
disponibles en un repositorio de Maven en la nube a un servidor Maven propio donde se
centraliza las versiones utilizadas en el proyecto sin repositorio en la nube, el mismo es público
por lo que cualquiera que descargue el proyecto podrá, sin problemas, desde el pom construir
el proyecto y descargar las dependencias requeridas.
78
<!-- Maven Repository on Git-BitBucket -->
<repositories>
<repository>
<id>maven-repo</id>
<layout>default</layout>
<url>https://bitbucket.org/facundoklaver/maven-
repo/raw/master/repository</url>
</repository>
</repositories>
79
5.5.5.3. Validación de escenarios.
A partir de las refactorizaciones implementadas en estos tres escenarios se han obtenido
varias mejoras en la calidad del proceso de desarrollo. A través de las mismas se logra una
reducción del tiempo de configuración del ambiente de desarrollo para una nueva terminal del
trabajo. De esta manera el proceso de desarrollo resulta más eficiente ya que se disminuye el
overhead producido por en el tiempo de startup.
Es por esto que, con un proyecto reestructurado y con algo más de conocimiento del
sistema, es pertinente realizar una Iteración 2 del proceso para atacar los problemas de
modificabilidad a nivel de arquitectura, diseño y/o implementación.
80
Figura 5.8. Diagrama de Clases antes de implementar Escenario 3.1.1.
Si bien la documentación del proyecto es escasa, se han realizado diagramas que permiten
comprender mejor el grado de acoplamiento existente entre el modelo de datos y las
funcionalidad de SocialGraph. Por ejemplo, en la figura 5.8, se ve cómo la construcción del
grafo está atada a la clase Mail. En general, la construcción, la importación y las principales
clases del modelo están vinculadas a la clase Mail, lo cual evidencia un fuerte acoplamiento y
la necesidad de realizar cambios en el diseño que permitan pensar el grafo como una
estructura independiente de los correos electrónicos.
81
5.6.1.2. Detección de code smells
Al momento de entrar en la iteración, se detecta un total de 132 code smells en el código del
sistema. En la tabla 5.8 se muestra la distribución según tipos de smell. Se observa que tres
tipos concentran 119 smells, es decir, el 90,15% del total de smells. A su vez, según el
agrupamiento de tipos, 69 (52,27%) son anomalías de identidad, 61 (46,21%) anomalías de
colaboración y 2 (1,52%) anomalías de clasificación. Estos datos indican qué tipos de
anomalías presenta el sistema en su estado actual, pero también es deseable conocer dónde
se localizan estos síntomas.
En la tabla 5.9 se muestra la distribución de smells por cada componente del sistema.
Descartando la interfaz gráfica del sistema, que no es de interés en esta iteración del proceso,
se ve que la gran mayoría de los smells se aloja en tres componentes: importer, graph y model.
Allí se acumulan 83 smells (62,87%). Es decir, esto confirma la necesidad de aplicar
refactorizaciones en estas áreas del sistema, responsables de la importación, la construcción
del grafo y el modelado de estructuras respectivamente.
Importer 43 32,58
GUI 36 27,27
Graph 25 18,94
Model 15 11,36
Otros 13 9,85
82
5.6.1.3. Identificación de los problemas en el diseño
Esta etapa de refactorización, se realiza una abstracción del modelo, que hasta el momento
está instanciado exclusivamente en correos electrónicos. Se observa que muchas de las clases
ligadas a la importación de datos y la construcción del grafo están íntimamente ligadas a la
clase Mail, que modela cada correo electrónico y por eso es necesario elevar el nivel de
abstracción, mejorando la calidad del diseño y la modificabilidad del sistema.
Los problemas de diseño detectados al analizar las relaciones entre clases, jerarquías e
interfaces muestran siempre el mismo síntoma: el sistema fue desarrollado para operar
exclusivamente con correos electrónicos. Por lo tanto, las refactorizaciones se deben centrar en
elevar el nivel de abstracción para soportar, en un futuro, nuevas fuentes de información que
puedan complementarse con los correos electrónicos.
83
el grafo y sus componentes depende directamente de cómo están modelados esos
componentes. En este caso los arcos, vértices y metadatos del grafo están diseñados para
operar sólo con correos electrónicos, por lo tanto las refactorizaciones no sólo deben realizarse
sobre la clase que “arma” el grafo, es necesario también aplicar cambios en las estructuras con
las que esta opera.
El objetivo en este caso es elevar el nivel de abstracción de modo que el armado del grafo
pueda realizarse con distintas fuentes de datos, que se componen de distintos tipos de
estructuras. El patrón strategy sirve para definir una familia de algoritmos, encapsula cada uno
de ellos y los hace intercambiables, permitiendo que un algoritmo varíe independientemente de
los clientes que los usarán [Gamma1995]. Por eso se elige este patrón para el diseño de las
estrategias de construcción del grafo. Cada estrategia sabe cómo construir en el grafo en base
a la lógica y características de sus estructuras de datos. Asi, se podrá implementar de manera
flexible una nueva estrategia específica para cada fuente de datos que se desee incorporar a
SocialGraph. La tabla 5.10. muestra la de especificación de escenario 3.1.1.
Ambiente Implementación.
Tabla 5.10. Especificación Escenario 3.1.1: Aplicar patrón Strategy a la clase GraphBuilder.
84
5.6.2.2.3. Escenario 3.1.2: Abstraer la clase CommunicationEdge de la clase
Mail en el modelo
La clase CommunicationEdge modela los arcos del grafo y su comportamiento a partir de los
mensajes entre dos o más personas. Al igual que otras clases del modelo,
CommunicationEdge sólo permite utilizar los mails como fuente de información, por lo tanto se
busca realizar una refactorización de modo que los arcos del grafo se puedan modelar con
otros tipos de datos. La tabla 5.11. muestra la de especificación de escenario 3.1.2.
Artefacto Clases:
model.CommunicationEdge
model.CommunicationLine
model.CommunicationLineTransformer
model.ComunicationLine
model.Mail
emaildump.AnalizadorSemanticoProcesoSoftware
graph.MailGraphBuilder
graph.PositionFinder
graph.TagFinder
graph.gui.SocialGraphGUI
graph.gui.popup.CommunicationEdgePopUpMenu
graph.io.GraphPersistenceHelper
graph.transformer.OnlyActorsGraphTransformer
graph.transformer.OnlyFilesGraphTransformer
Ambiente Implementación.
85
clase PersonVertex y las clases relacionadas. La tabla 5.12. muestra la de especificación de
escenario 3.1.3.
Artefactos Clases:
model.PersonVertex
graph.MailGraphBuilder
graph.PositionFinder
graph.gui.popup.PersonVertexPopUpMenu
graph.io.GraphPersistenceHelper
profile.ProfileBuilder
Ambiente Implementación.
Artefacto Clases:
86
model.AttachmentEdge
model.FileVertex
graph.MailGraphBuilder
graph.gui.popup.AttachmentEdgePopUpMenu
graph.gui.popup.FileVertexPopUpMenu
graph.io.GraphPersistenceHelper
Ambiente Implementación.
Artefacto Clases:
graph.TagFinder
model.Tag
profile.ProfileBuilder
Ambiente Implementación.
87
Respuesta Realiza modificaciones sin afectar funcionalidades del sistema.
Tabla 5.14. Especificación de escenario 3.1.5: Abstraer la clase TagFinder de la clase Mail en el modelo.
Ambiente Implementación.
88
La solución propuesta en este escenario es abstraer la clase Mail de la vista y que esta
quede en función de CommunicacionLine de forma que no esté ligada a una fuente de
información en particular. Para esto se plantea el escenario correspondiente y se analizan los
code smells asociados a las clases de la vista que hay que modificar para poder indicar
posibles refactorings a realizar sin perder de vista el problema de fondo ni los atributos de
calidad que nos guían en el proceso. La tabla 5.16. muestra la de especificación de escenario
3.2.
Artefacto graph.gui.popup.FileVertexPopUpMenu.java
graph.gui.popup.AttachmentEdgePopUpMenu.java
graph.gui.popup.CommunicationEdgePopUpMenu.java
graph.gui.SocialGraphGUI.java
profile.ProfileBuilder.java
graph.gui.popup.PersonVertexPopUpMenu.java
graph.gui.panel.MailBrowserPanel
graph.gui.panel.MailProcessorPanel
profile.ProfileBuilder
Ambiente Implementación.
Tabla 5.16. Especificación de escenario 3.2: Abstraer la clase Mail en la interfaz gráfica de la herramienta.
89
con funcionalidades similares, con alto acoplamiento, desarrolladas a demanda, en distintas
etapas del ciclo de vida del sistema y por distintos desarrolladores. La tabla 5.17. muestra la de
especificación de escenario 3.3.
Ambiente Implementación.
Con menor grado de impacto, se encuentra el escenario 3.3, que requiere revisar la
funcionalidad de importación del sistema y por último el escenario 3.2 que propaga las
abstracciones del modelo a la interfaz gráfica del sistema.
90
5.6.3.2. Nivel de Esfuerzo
El nivel de esfuerzo varía según las capacidades y el conocimiento que tengan del sistema
los desarrolladores. Esta variable se puede medir a partir de los tiempos requeridos para
implementar cada escenario. Según estos tiempos estimados, el orden de escenarios es el
siguiente:
91
A partir de la valoración de los escenarios, se los ubica en el cuadrante de deuda técnica que
se muestra en la figura 5.9.
92
Figura 5.10. Diagrama de Clases después de implementar Escenario 3.1.1.
93
mayoría de estos code smells se resuelve fácilmente reorganizando el código, se siguen los
pasos para refactorizar de la guia de Lanza y Marinescu [Lanza2007] para atacar cada code
smell. Se describen los más relevantes a continuación:
94
dominó y retraso del tiempo de binding. Se agregan atributos dinámicos de modo que
se optimiza el polimorfismo y se minimiza el impacto en la interfaz gráfica y otras áreas
del sistema. Esto puede considerarse un paso intermedio para otro refactoring.
La implementación del adaptador trae varias ventajas. Por un lado se contiene una
enorme cantidad de conflictos que surgen en todos el sistema al reemplazar la clase
Mail por la interfaz CommunicationLine los posibles. En cada caso habrá que invocar al
adaptadorpara obtener una lista de Mail o una lista de CommunicationLine, ya que
mientras dure el proceso de refactorización y como sugieren las tácticas de
modificabilidad, ambas interfaces conviven en el sistema. Por otro lado, las referencias
a CommunicationLineTransformer representan “tareas pendientes” de abstracción o
refactorización del sistem que deberán abordarse en un futuro pero que ahora serán
fáciles de identificar.
95
5.6.4.2.3. Ejecutar casos de test
Se ejecutaron los casos de test correctamente, verificando la no introducción de cambios en
la funcionalidad del sistema.
Esta extensa y compleja clase presenta muchos síntomas que, combinados, llevan a
identificarla como God Class. Por ejemplo, varios de métodos deberían ubicarse en otras
clases, como la clase Mail. Dada la falta de abstraccion y polimorfismo en varios puntos se
realizan invocaciones tipo “instance of” que podrían evitarse. También se detecta mucho código
muerto que no es invocado en ningún momento, entre otras malas prácticas de programación.
• Move Method: getStart, getEnd y filter se movieron a la clase Mail. Se eliminan 2 smells:
God Class y Shotgun Surgery en Mail.getSendDate.
• Eliminación de método Main. Se elimina un smell: Feature Envy en método Main.
• Move Method: increment, formatDate y formatSlice se mueven a la nueva clase
DateUtils del nuevo paquete edu.isistan.socialgraph.utils. No se eliminan smells.
• Eliminación de clase DegreeComparator. No era utilizada en el proyecto.
• Eliminación de métodos: generateSimpleGraph y generateSlicedGraph. No eran
utilizados.
• Eliminación del método testRead. No era utilizado. Imprimía por pantalla los
componentes de grafo. Se eliminan 2 smells.
• Extract method: getEdgeProperties y getVertexProperties.
96
• Se implementa una refactorización de modo que cada Vertex o Edge del grafo sepa
cómo cargar y/o retornar sus propiedades. Se crean métodos abstract en las clases
AbstractVertex y AbstractEdge. De esta manera se eliminarán las sentencias de tipo
“instance of” en la clase GPH. Refactorizaciones implementadas:
◦ Se crean los métodos en AbstractVertex e hijos:
▪ public abstract void readProperties(Map<String, Object> properties);
▪ public abstract void writeProperties(Map<String, Object> properties);
◦ Se eliminan los últimos dos smells de GPH, de tipo Dispersed Coupling, ranking 20 y
21.
Como resultado, se obtiene una clase más limpia, cohesiva y menos compleja. Se reduce
una gran cantidad de smells (7), incluyendo el God Class.
Estos dos test se ejecutaron con éxito en un primer momento. Luego de unos días de
desarrollo, utilizando la aplicación se notó que uno de las partes de la interfaz gráfica no estaba
funcionando correctamente. Al depurar el código, detectó un error en una área del código
afectada por este refactoring. Dada esta situación, se comprueba que la cobertura de test no
estaba garantizada y por lo tanto, se procede a crear un nuevo caso de test. Se crea el test
“readGraphBuilder” que lee el grafo creado con en el test “testBuildGraph” y verifica los
parametros del grafo que no se tuvieron en cuenta en el anterior test.
97
De esta experiencia se puede aprender que es más peligroso pensar que se tiene una
cobertura del 100% en el área de código a modificar, que saber que se está modificando código
sin cobertura, ya que resulta en una falsa seguridad y es un riesgo en el proceso de desarrollo
del cual no se tiene conocimiento. También hay que tener en cuenta que en un proceso de
refactoring cuando antes se encuentre un bug mas facil es resolverlo ya que el desarrollador
tiene más fresco el área de código comprometida y cuáles fueron las modificaciones realizadas.
+ mailsList.addAll(((CommunicationEdge) edge).getMails());
[...]
A través de la interfaz CommunicationLine se puede tener mayor flexibilidad con los atributos
dinámicos para la vista. Sin embargo, en un futuro se tendrá que realizar una separación de las
98
capas Modelo y Vista para que esta última sea más modificable, tal como se describe en la
Anomalía 2.
Al agregar una nueva fuente de información, si los atributos a mostrar fueran los mismos o
parecidos la modificación realizada en este escenario, el diseño actual podría soportarlo. En
caso contrario, si esta debe ser mostrada de una forma diferente a la ya existente, se deberá
seguir refactorizado la aplicación. En tal caso, habría que modificar la arquitectura para que se
visualicen los atributos según el tipo de fuente de información, para esto se requiere separar la
Vista del Modelo e implementar la lógica en el Controlador.
● importer.Importer (4 smells)
● emaildumpapp.ImportThunderbirdEmail (8 smells, contiene God Class)
● importer.ImporterHelper (9 smells, contiene God Class).
● importer.MailPersistenceHelper (1 smell)
● importer.docreader.DocumentReader (1 smell, existe dead code)
● importer.FileTag.FileTagProvider (4 smells, existe dead code)
● emaildumpapp.EmailDumpApp (1 smell)
● emaildumpapp.TableEmailPanel (1 smell)
● emaildumpapp.EmailDumpPanel (1 smell)
● emaildumpapp.EmailMergePanel (1 smell)
● emaildump.AnalizadorSemanticoProcesoSoftware (3 smells)
99
● emaildump.EmailContent (1 smell)
● emaildump.EmailDump (5 smells, God Class)
● emaildump.StringUtils (1 smell)
Con esta serie de cambios se anularon todos los smells en la clase EmailDump, sin
embargo, se introdujeron nuevos smells en la clase Mail, destacándose God Class. Entonces,
en esta instancia se elimina el mismo tipo de smell de una clase mientras que se lo incorpora
en otra. La refactorización vale porque el código resulta menos acoplado, más comprensible y
por lo tanto, más fácil de modificar.
100
vii Dispersed Coupling ImportThunderbirdEmail.importrEMLmailToMessageCleanConcurrente 55
Luego de estas refactorizaciones sólo quedan los smells vi y vii, ambos en el método
importEMLmailToMessageCleanConcurrente, uno de los más importantes de esa clase. En el
proyecto restan 116 smells.
101
viii Dispersed Coupling ImporterHelper.getAttachments 31
Dado que ambas clases sólo son instanciadas en métodos main, seguramente para testing
en alguna otra etapa de desarrollo de SocialGraph, se consideran clases obsoletas, código
técnicamente muerto, y por lo tanto son eliminadas del proyecto. También se elimina el método
main de la clase MailPersistenceHelper. Naturalmente, se reducen todos los smells de las
clases Importer e ImporterHelper y además 1 smell en MailPersistenceHelper. En el proyecto
quedan 93 code smells.
102
5.6.4.6.3. Ejecutar casos de test
Se ejecutaron los casos de test correctamente, verificando la no introducción de cambios en
la funcionalidad del sistema.
En esta segunda iteración del proceso, se han definido e implementado ocho escenarios de
modificabilidad a través de los cuales se ha atacado la Anomalía 3: “Diseño ligado a correos
electrónicos”. Por lo tanto, habrá que verificar si el diseño de la aplicación es ahora, un poco
menos ligado a correos electrónicos, es decir, un poco más abstracto y más modificable.
• Code smells por tipos. En la tabla 5.19 se aprecian las mejoras realizadas en esta
iteración en cuanto a los code smells de todo el sistema. Se observa un descenso
notable de la cantidad total de smells (29,55%) y mejoras significativas en los
principales tipos de smells vinculados a acoplamiento y cohesión. El tipo de smells con
mayor mejora (75%) son las God Class, que se asocia a clases con alta complejidad,
alto acoplamiento y baja cohesión.
Tabla 5.19. Variación de code smells de cada tipo antes y después de iteración 2.
103
• Code smells por componentes afectados. La tabla 5.20 muestra las mejoras antes y
después de las refactorizaciones en cada uno de los principales componentes del
sistema. Se destaca el mejor resultado relativo en Importer donde, luego del escenario
3.3, se logra bajar de 43 a 17 smells. Luego los componentes Graph (reducción de 14
de 25 smells) y Model (reducción de 4 de 15 smells) siguen en nivel de mejora. Como
se vió en la etapa 1 de esta iteración, estos tres componentes, que concentran la
mayoría de los smells, son los responsables de la construcción del grafo, el modelado y
la importación de datos. Estas áreas de código tienen relación directa con el desarrollo
de los requerimientos funcionales planificados para SocialGraph. Por lo tanto, es
fundamental lograr allí estas mejoras de modificabilidad.
Se observa también que algunos smells no fueron eliminados, sino desplazados a otras
áreas del sistema. En particular los 5 “nuevos” smells que figuran en la fila Otros se
ubican en áreas de menor importancia: tres de ellos en los casos de test y dos el
paquete utils.
Importer 43 17 60,47%
GUI 36 36 0,00%
Graph 25 11 56,00%
Model 15 11 26,67%
Otros 13 18 -38,46%
Tabla 5.20. Variación de code smells en cada paquete antes y después de iteración 2.
104
fuente, bastará con crear una nueva estrategia de construcción del grafo, que puede
combinarse con la(s) existente(s).
Esta mejora de diseño se complementa con la abstracción del modelo en los escenarios
3.1.2 a 3.1.5. Alli, se reemplazó en cada uno de los componentes del grafo, la clase Mail por la
interfaz CommunicationLine, que puede ser implementada por diversas fuentes de información.
A su vez, este cambio se propagó a la interfaz gráfica en el escenario 3.2. De modo que se
elevó el nivel de abstracción en todas las secciones de código vinculadas a la construcción del
grafo.
Se ha aplicado una segunda iteración del proceso definido sobre el caso de estudio. Esto
permitió conocer en profundidad las principales defectos de diseño e implementación del
sistema para aplicar refactorizaciones y obtener mejoras en la modificabilidad del sistema.
Dado el esfuerzo dedicado y el impacto positivo que se obtuvo, se resuelve no realizar una
tercera iteración del proceso y dar por terminada la actual instanciación del mismo.
105
6. Conclusiones
La refactorización es el proceso de modificar un programa para mejorar la calidad estructural
de su código fuente sin alterar su funcionalidad. Esos cambios se pueden dar de manera
aleatoria, no planificada o bien se pueden direccionar los esfuerzos para que la mejora de
calidad sea más valiosa. En este trabajo final se buscó organizar un conjunto de buenas
prácticas para brindar a los desarrolladores un proceso guiado por la modificabilidad del
sistema. Para ello, se combinaron distintas herramientas y metodologías en un proceso paso a
paso que facilita el seguimiento y la evaluación del proceso de refactorización.
La etapa de análisis permitió detectar los principales problemas del sistema para luego idear
las refactorizaciones que se debían implementar. Por eso fue fundamental tener un buen
conocimiento del sistema, contar con documentación actualizada, valerse de métricas y otras
herramientas de análisis. En el caso de estudio, los requerimientos funcionales estuvieron
claros, además se contaba con cierto conocimiento de la arquitectura del sistema, aunque este
se debió profundizar y documentar a través de diagramas UML para comprender las
estructuras y el flujo de datos. El uso de code smells, sugerido por el proceso, fue esencial para
detectar síntomas de problemas en componentes clave de la arquitectura. Así se detectó el
grave acoplamiento entre artefactos que desde el punto de vista del diseño, quizás no debieran
estar tan vinculados. Este tipo de métricas son útiles cuando el sistema es relativamente
grande y no se tiene conocimiento de sus detalles de implementación.
106
definidos no fue homogénea, ya que en algunos casos se trató de refactorizaciones concretas a
nivel de código pero en otros se requirió profundizar el análisis del diseño incluso durante la
etapa de implementación. En general, esto varía según la complejidad del escenario y la
experiencia de los desarrolladores que lo implementan. En algunos casos se optó por dividir el
escenario en sub-escenarios de manera tal de minimizar el riesgo de sufrir un efecto dominó y
resolver el problema a través de la combinación de varias soluciones.
107
beneficios de enmarcar los esfuerzos de desarrollo en este proceso permiten identificar mejor
las áreas prioritarias para aplicar las refactorizaciones y, como consecuencia de ese análisis,
sugerir soluciones acertadas a implementar.
Los resultados obtenidos a partir del caso de estudio son alentadores para seguir aplicando
este proceso para mejorar la calidad de otros sistemas y también del proceso en general. En su
definición, se especificó una serie de etapas y actividades, brindando un marco flexible para
que los analistas incorporen sus propias herramientas para el análisis y medición de resultados.
6.3. Limitaciones
A partir de la aplicación del proceso definido en el caso de estudio, se detectan algunas
limitaciones que es bueno tener en cuenta en otros casos de aplicación:
• Es difícil garantizar en un 100% la cobertura de tests para poder evitar posibles efectos
dominó al realizar una refactorización. Por ejemplo, en el escenario 3.1.6: “Refactorizar
God Class en GraphPersistenceHelper” se ejecutaron los tests correctamente pero días
más tarde, por casualidad, se detectó un error en el sistema que fue introducido durante
esta refactorización. Esta situación requirió dedicar tiempo a rastrear el origen del error,
corregirlo y además, volver revisar la forma en que se testea el sistema.
• El éxito de la instanciación de este proceso está ligado a las características del sistema
de software al que se aplica: la escala del sistema, la calidad de arquitectura y los
conocimientos del mismo por parte de los desarrolladores responsables del refactoring.
Estos factores son claves para el éxito en las etapas de análisis y especificación de
escenarios y pueden determinar la viabilidad de todo el proceso.
• La experiencia del caso de estudio demuestra que los code smells ayudan a detectar
síntomas de malas prácticas de implementación, sin embargo también se detectan
muchos falsos positivos que obligan a los desarrolladores a revisar cada uno de los
smells detectados. Para el diagnóstico de la arquitectura y el diseño no alcanza con el
análisis objetivo que puede hacerse sobre las métricas. También es imprescindible el
análisis subjetivo obtenido a partir de la experiencia de los desarrolladores en la
evaluación y toma de decisiones. Es necesario contar con uno o más desarrolladores
involucrados con la arquitectura del sistema y los objetivos del proyecto.
108
• Naturalmente, la realización de este proceso no soluciona todos los problemas de un
sistema en desarrollo, más bien ayuda a dedicar los recursos a mejoras estratégicas
para la evolución del sistema. Queda evidenciado que el refactoring debe ser un
proceso continuo en el tiempo, donde se mejore la calidad del software de manera
constante.
• Seguramente el proceso puede ser mejorado en cada una de sus etapas y actividades.
Las técnicas, métodos, indicadores y herramientas utilizados pueden ser refinadas o
bien se pueden incorporar nuevas opciones que aporten a un proceso de mayor calidad.
• Parte del análisis realizado en el proceso definido es denominado subjetivo, con lo cual
es difícil de medir y podría arrojar resultados dispares dependendiendo de quién o
quiénes lo lleven a cabo. A futuro sería bueno reforzar las herramientas que asisten a
los desarrolladores en la toma de decisiones para el análisis y la validación del proceso.
Cada experiencia de refactorización tiene sus particularidades, por lo que es deseable contar
con un proceso que pueda adaptarse a la mayor variedad de sistemas y metodologías de
desarrollo a fin de proveer un proceso consistente que garantice una mejora de calidad tanto a
nivel de código, como de diseño y de arquitectura.
109
7. Referencias
[Alshayeb2009] Alshayeb, M. (2009). Empirical investigation of refactoring effect on software
quality. Information and software technology, 51(9), 1319-1326.
[Bass2003] Bass, L., Clements, P., & Kazman, R. (2003). Software Architecture in Practice.
[Bavota2015] Bavota, G., De Lucia, A., Di Penta, M., Oliveto, R., & Palomba, F. (2015). An
Experimental Investigation on the Innate Relationship between Quality and Refactoring. Journal
of Systems and Software.
[Bengtsson2000] Bengtsson, P., Lassing, N., Bosch, J., & van Vliet, H. (2000). Analyzing
software architectures for modifiability.
[DuBois2004] Du Bois, B., Demeyer, S., & Verelst, J. (2004, November). Refactoring-
improving coupling and cohesion of existing code. In Reverse Engineering, 2004. Proceedings.
11th Working Conference on (pp. 144-151). IEEE.
[Elssamadisy2002] Elssamadisy, A., & Schalliol, G. (2002, May). Recognizing and responding
to bad smells in extreme programming. In Proceedings of the 24th International conference on
Software Engineering (pp. 617-622). ACM.
[Frier2015] Frier, J. R., & Roggio, R. F. (2015). The Downsides of Software Refactoring.
Journal of Computer Science, 3(1), 01-13.
[Gamma1995] Patrones de Diseño. Erich Gamma, Richard Helm, Ralph Johnson, John
Vlissides, Addison Weasley. 1995.
[Garcia2009] Garcia, J., Popescu, D., Edwards, G., & Medvidovic, N. (2009, March).
Identifying architectural bad smells. In Software Maintenance and Reengineering, 2009.
CSMR'09. 13th European Conference on (pp. 255-258). IEEE.
110
[ISO9126] ISO/IEC 9126-1, Software engineering – product quality – Part 1: Quality Model,
first ed.: 2001-06-15.
[Kim2012] Kim, M., Zimmermann, T., & Nagappan, N. (2012, November). A field study of
refactoring challenges and benefits. In Proceedings of the ACM SIGSOFT 20th International
Symposium on the Foundations of Software Engineering (p. 50). ACM.
[Lanza2007] Lanza, M., & Marinescu, R. (2007). Object-oriented metrics in practice: using
software metrics to characterize, evaluate, and improve the design of object-oriented systems.
Springer Science & Business Media.
[Liu2006] Liu, J., Batory, D., & Lengauer, C. (2006, May). Feature oriented refactoring of
legacy applications. In Proceedings of the 28th international conference on Software
engineering (pp. 112-121). ACM.
[Liu2009] Liu, H., Yang, L., Niu, Z., Ma, Z., & Shao, W. (2009, August). Facilitating software
refactoring with appropriate resolution order of bad smells. In Proceedings of the the 7th joint
meeting of the European software engineering conference and the ACM SIGSOFT symposium
on The foundations of software engineering (pp. 265-268). ACM.
[Liu2012] Liu, H., Gao, Y., & Niu, Z. (2012, July). An initial study on refactoring tactics. In
Computer Software and Applications Conference (COMPSAC), 2012 IEEE 36th Annual (pp.
213-218). IEEE.
[Mens2004] Mens, T., & Tourwé, T. (2004). A survey of software refactoring. Software
Engineering, IEEE Transactions on, 30(2), 126-139.
[MurphyHill2008] Murphy-Hill, E., & Black, A. P. (2008). Refactoring tools: Fitness for purpose.
Software, IEEE, 25(5), 38-44.
[Schulz2009] Schulz, C., Löwe, M., & König, H. (2009). Refactoring object-oriented systems.
Manipulation of Graphs, Algebras and Pictures. Essays Dedicated to Hans-Jörg Kreowski on
the Occasion of His 60th Birthday, Universität Bremen, 321-340.
111
[Shrivastava2008] Shrivastava, S. V., & Shrivastava, V. (2008, November). Impact of metrics
based refactoring on the software quality: A case study. In TENCON 2008-2008 IEEE Region 10
Conference (pp. 1-6). IEEE.
[Simon2001] Simon, F., Teinbruckner, F. S., & Lewerentz, C. (2001). Metrics based
refactoring. In Software Maintenance and Reengineering, 2001. Fifth European Conference on
(pp. 30-38). IEEE.
[Tourwé2003] Tourwé, T., & Mens, T. (2003, March). Identifying refactoring opportunities using
logic meta programming. In Software Maintenance and Reengineering, 2003. Proceedings.
Seventh European Conference on (pp. 91-100). IEEE.
[VanEmden2002] Van Emden, E., & Moonen, L. (2002). Java quality assurance by detecting
code smells. In Reverse Engineering, 2002. Proceedings. Ninth Working Conference on (pp.
97-106). IEEE.
[Wasserman1994] Wasserman, S., & Faust, K. (1994). Social network analysis: Methods and
applications (Vol. 8). Cambridge university press.
112