Capítulo 4: Diseño e Implementación
4.1.2 El Motor de métricas
4.1.2.1 Unificación de los datos
El Motor de métricas es el encargado de interpretar la información, relacionando las métricas provenientes de cada conector con el fin de unificarlas para maximizar la cohesión de las mismas, además, es capaz de inferir información adicional mediante el Administrador de reglas. En el mismo residen tres problemas que deben ser resueltos desde el diseño del mismo. El primer y principal problema es el de unificar los datos proveniente de cada uno de los conectores. El segundo problema es que al no conocer el flujo de datos de las herramientas del entorno de desarrollo, debe diseñarse de forma que pueda escalar. Mientras que el último consiste en cómo almacenar la información obtenida de forma correcta.
4.1.2.1 Unificación de los datos
Unificar todos los datos obtenidos de diferentes conectores en un mismo lugar correspondiente a la tarea que representa fue uno de los principales problemas a solucionar dentro del sistema. Esta tarea consistió en realizar un algoritmo que sea capaz de juntar todos los datos teniendo en cuenta que algunos conectores pueden variar en el conjunto de métricas que proporcionan e incluso nuevos conectores pueden ser agregados al sistema proporcionando nuevas métricas que el sistema debe tener que poder integrar con los demás datos de manera sencilla.
Para poder lograr dicho objetivo se utilizó el patrón de diseño Decorator (Gamma, et al., 1995), en donde cada parte es encargada de agregar sus datos a una tarea dada, así, se tiene un componente que dada una tarea el mismo obtienen los datos que están guardados en la base de datos acerca de la misma y los incorpora de la manera que corresponda en cada caso. El componente que siempre debe estar presente es el que agrega los datos de la herramienta del Issue Tracker ya que de la misma se obtiene el identificador de tarea junto con el usuario que realiza la misma. La Figura 4.5 muestra un diagrama de clase reducido de cómo cada componente se encarga de agregar los datos que correspondan y de informar al sistema que tipos de métricas van a ser agregar.
Figura 4.5: Diagrama de clases de estructura que agrega métricas a una tarea.
Esta estructura da soporte al algoritmo que unifica los datos. El algoritmo comienza cuando se invoca al método aggregateMetrics, el mismo llama recursivamente a este método de cada decorador antes de realizar alguna acción. El último método que será llamado corresponde al del Issue Tracker y este será el primero en agregar los datos a la estructura que almacena los datos, luego mientras se vuelve en la recursión la estructura es completado por cada clase aggregator (que representan un conector genérico) hasta que cada conector termina de agregar los datos que poseen. Como esta estructura es compleja para inicializar y resulta útil utilizar varias diferentes en el caso que se quieran generar análisis personalizados, se inicializa mediante un Factory (Gamma, et al., 1995) en particular que define de qué forma serán construidos los datos de una tarea. Este Factory se muestra en la Figura 4.6.
Figura 4.6: Diagrama de clases de Factory del agregador
De esta manera, si se agrega un nuevo conector al sistema hay que definir cómo integra los datos el mismo a las tareas, mediante la creación de una nueva clase que indique cómo se integran y un nuevo Factory que indique cómo se relaciona como los demás, sin necesidad de modificar código.
Una característica que resulta importante analizarla son los tipos de datos de cada métrica, ya que los mismos son impuestos por las herramientas. Los tipos de datos más común son entero, estos tipos de datos se encuentran dispersos en prácticamente todas las herramientas ya que representan cantidades, de líneas, de tareas, de commits , etcétera. Sin embargo existen tipos de datos más complejos como el tiempo que lleva una tarea, la distribución de comentarios en el código, entre otros. Para poder almacenar y operar estos datos se utiliza un manejador particular para cada uno. Un manejador no es más que una clase que almacena el valor de un dato particular y dado otro dato del mismo tipo lo opera. Los manejadores se definen como indica la Figura 4.7, esto trae la ventaja que si una nueva herramienta se quiere agregar con nuevos tipos de datos solo hay que extender la interfaz Metrics. Para poder relacionar cada tipo de dato con su respectivo manejador primero se pide al conector específico que informe sobre cada dato que es capaz de proporcionar y su correspondiente tipo de dato, luego el tipo de dato se mapea al manejador mediante el uso de reflexión (Smith, 1982) entonces si el tipo de dato se llama “ INT ” se crea una instancia de la clase INT para que maneje este dato.
Figura 4.7 Diagrama de clases de mapeo por reflexión de tipos de datos
4.1.2.2 Escalabilidad del sistema
La escalabilidad del Motor de métricas es crucial para el funcionamiento de Tesys. Esto se debe a que el Motor de métricas puede llegar a recibir mucha información desde los distintos conectores ya que la actividad de los mismos depende de qué tan grande y activo sea el equipo de desarrollo. Es crucial que el mismo pueda soportar todas las peticiones que cada uno realiza sin retener por mucho tiempo una petición o perder alguna.
La solución a este problema viene de la mano con la decisión de utilizar REST ya que resultó relativamente simple implementar un manejo de hilos (Lewis, et al., 1995) en cada componente, principalmente en el Motor de métricas. Cada petición que llega desde cualquier componente o otro es tratada en un hilo independiente, además varias peticiones se atienden