Caso de estudio #2: Visualización
Aplicación: Health Watcher
Este estudio evalúa si las mejoras de visualización de resultados implementadas sobre la herramienta JSpIRIT en este trabajo aportan información relevante que ayude a detectar problemas en la arquitectura de los sistemas y/o problemas no detectados utilizando la lista de resultados que provee la versión original de la herramienta. Este contexto es particularmente aplicable en casos en que los análisis arrojan una gran cantidad de smells y aglomeraciones, y el desarrollador necesita tener una perspectiva del estado general del sistema. El análisis de los code smells y las aglomeraciones permite a los desarrolladores entender los problemas potenciales para decidir si deben o no ser refactorizados. Este experimento se llevó a cabo en Health Watcher, una aplicación web cuyo objetivo es permitir a los ciudadanos registrar quejas acerca de problemas de salud en instituciones públicas. La aplicación contiene 135 clases y JSpIRIT arrojó un total de 58 smells y 82 aglomeraciones en el análisis.
7.1 JSpIRIT Análisis de code smells
A continuación se realiza un análisis de los smells detectados por JSpIRIT tomando el rol de un desarrollador en la interpretación de los resultados obtenidos.
7.1.1 Vista de Heat Map
Si bien el muestreo de resultados tradicional extrae los smells que se consideran relevantes, no ofrece una perspectiva general del sistema. Cuando el desarrollador navega los elementos de código afectados, su visión puede verse
reducida a solucionar los problemas que encuentra en cada clase y continuar con el siguiente smell. Existen ocasiones en las cuales un problema puede superar el scope de una clase, es decir, expandirse a una jerarquía de clases e incluso pudiendo afectar a otros componentes que hacen uso de las mismas. Si bien el smell es un indicador de que puede existir un problema en una clase esto no significa que termine ahí. Es posible que el smell se contagie a todo un componente o a otros sectores del sistema implicando malas decisiones en el diseño del mismo. Por esta razón, es de utilidad tener una visión general del sistema que provea otra perspectiva, en otras palabras que además de poder analizar las partes sea posible analizar el todo. La Figura 7.1a muestra una vista de “puntos calientes” del sistema desarrollada en este trabajo e introducida en el Capítulo 5 con este objetivo. Según la visualización (Figura 7.1a) el paquete más afectado del sistema es healthwatcher.view.command. El paquete tiene un total de 35 clases y presenta un total de 15 smells. El gran número de clases que contiene el paquete no es un buen indicador [76], ya que puede significar que el componente posee muchas responsabilidades. La visualización de Heat Map además agrupa y provee un detalle de las clases afectadas en el paquete en cuestión y los smells afectantes (Figura 7.1b). Explorando la lista de smells de este paquete provista por la visualización (Tabla 7.2) se puede ver que 2 de estos corresponden a los smells #3 y #5 rankeados por JSpIRIT en su vista de smells tradicional. Incluyendo a estos dos, 12 de los 15 smells encontrados en el paquete comparten una particularidad, todos afectan a métodos bajo el mismo nombre, execute(). Como esto raramente podría ser una casualidad es necesario analizar las responsabilidades asociadas a estos métodos y su relación en caso de que existiera. Muchos de los smells encontrados son del tipo Dispersed Coupling sobre el método execute() lo que sugiere que el método utiliza operaciones definidas en varias clases del sistema.
Figura 7.1a: Health Watcher Vista de Heat Map
Navegando sobre las clases detalladas por la visualización para el paquete
healthwatcher.view.command (Tabla 7.1) se observa que la gran mayoría extiende a una clase abstracta Command que se encuentra en el mismo paquete y define un método abstracto execute() para ser implementado por sus subclases, en total 32. Estas clases intentan seguir el patrón de diseño “Command”, el hecho es que el comportamiento de los métodos debería diferir de una clase a otra, sin embargo muchas de ellas presentan un comportamiento similar, incluso muchas de ellas comparten en su nombre el mismo prefijo, haciendo alusión a que tal vez ejecutan operaciones similares sobre entidades diferentes. En efecto, cada una de estas 32 clases realiza una operación de inserción, actualización o búsqueda sobre una base de datos en base a una petición recibida. Probablemente sería posible reducir notablemente la cantidad de subclases realizando una abstracción del comportamiento basado en el tipo de operación que realiza cada una. Code Smell Ranking Dispersed [email protected] 24 Dispersed [email protected] 27 Brain [email protected] 5 Intensive [email protected] 14 Dispersed [email protected] 35 Brain [email protected] 7 Dispersed [email protected] 40 Dispersed [email protected] 41 Dispersed [email protected] 43 Dispersed [email protected] 48 Shotgun [email protected] 21 Shotgun [email protected] 22 Shotgun [email protected] 23
Dispersed [email protected] 53 Dispersed [email protected] 54 Tabla 7.1: Code smells afectando al paquete healthwatcher.view.command.
Otro indicador que refuerza las sospechas de la existencia de un problema en el paquete son los smells encontrados del tipo Shotgun Surgery (Tabla 7.1). Como se dijo anteriormente cada una de las 32 clases que extienden a la clase
Command actúan en base a peticiones, estas son implementadas por la clase
CommandRequest, y sus instancias son utilizadas por las subclases de
Command, lo que significa que cualquier modificación en las signaturas de los métodos de CommandRequest impactaría en todas y cada una de estas subclases. En efecto, los smells del tipo Shotgun Surgery sobre un elemento de código (contrariamente al tipo Dispersed Coupling ) indican que cualquier modificación sobre este tendrá un gran costo asociado, debido a los cambios necesarios en una gran cantidad de clases que lo utilizan.
Luego de haber explorado un poco los elementos de código del sistema y de haber adquirido un mejor entendimiento del mismo se pudo establecer una aproximación a la arquitectura pretendida por sus desarrolladores. Incluso los prefijos de los nombres de los paquetes sugieren que el sistema contiene tres componentes principales que modelarían una arquitectura Cliente/Servidor por capas [77]: 1. Capa de presentación (healthwatcher.view.*) 2. Capa de negocio: (healthwatcher.business.*)
3. Capa de datos: (healthwatcher.data.* y healthwatcher.model.*)
En esta arquitectura (Figura 7.2) la capa de negocio actúa de intermediario entre las capas de presentación y de datos, esto produce una mejora en la modificabilidad del sistema, aislando la funcionalidad de cada capa y reduciendo el impacto ante un eventual cambio en una de ellas.
Figura 7.2: Arquitectura Cliente/Servidor de tres capas.
Para lograr este aislamiento entre las capas, cada una de ellas solo debe poder comunicarse con sus capas aledañas. Gracias a la visualización de las dependencias de los paquetes afectados provista por la vista de heat map (Figura 7.4) se pudo detectar que esto no se respetaba en el sistema Health Watcher. La Figura 7.3 muestra un diagrama de los módulos de Health Watcher, en líneas negras se representan las dependencias que deberían tener los módulos, mientras que en líneas punteadas de color rojo se representan las dependencias entre módulos que violan a la arquitectura pretendida. Figura 7.3: Vista de módulos de Health Watcher.
Como se observa en la Figura 7.4, el paquete healthwatcher.view.command que formaría parte del módulo GUI (Figura 7.3), tiene varias dependencias directas con paquetes de prefijo healthwatcher.model, que pertenecerían a la capa de datos, violando el diseño definido por la arquitectura. Figura 7.4: Violación en la arquitectura de Health Watcher.
Si el código se correspondiera con la arquitectura, todas las relaciones entre los paquetes healthwatcher.view.* y los paquetes healthwatcher.model.* deberían ser transitivas a través de cierta lógica implementada en los paquetes
de dependencia que parten desde paquetes healthwatcher.view.* hacia paquetes
healthwatcher.business.* por un lado, y por otro lado dependencias desde paquetes healthwatcher.business.* hacia paquetes healthwatcher.model.*.
El problema anterior está asociado con el manejo de excepciones. Por ejemplo, la clase SearchData invoca a diferentes servicios del módulo BUSINESS. SearchData luego termina manejando excepciones arrojadas por el módulo DATA, incluyendo aquellas que deberían ser tratadas internamente. Esto conlleva a un acoplamiento adicional entre los elementos que implementan los módulos DATA y GUI, resultando en una violación de la arquitectura.
En la vista de heat map también se puede visualizar otra violación de la arquitectura marcada en la Figura 7.3 que muestra una dependencia desde el módulo BUSINESS (correspondiente a la lógica del negocio) hacia el módulo GUI (Interfaz Gráfica de Usuario).
Figura 7.5: Violación en la arquitectura de Health Watcher.
Esta dependencia indeseada se puede visualizar en el heat map de la Figura 7.5 observando las dependencias del paquete healthwatcher.business. Allí se puede ver como el paquete healthwatcher.business del módulo BUSINESS utiliza al paquete healthwatcher.view perteneciente al módulo GUI.