Complejidad ciclomática
En este artículo analizaremos la métrica de complejidad ciclomática y cómo calcularla para una sección de código. También veremos la importancia de esta métrica en el desarrollo y testeo de aplicaciones y describiremos las técnicas más comunes para reducirla.
La Complejidad Ciclomática o complejidad McCabe fue propuesta por primera vez por Thomas McCabe en 1976. Se trata de una métrica de software que mide cuantitativamente la complejidad lógica de un programa o sección se código, y puede ser aplicada a cualquier lenguaje de programación. Esta métrica no indicará la longitud o tiempo de ejecución requerido para ejecutar un código, sino la complejidad derivada de los distintos caminos de ejecución que este puede tomar.
Diagrama de flujo
La complejidad ciclomática está se basa en el diagrama de flujo en el que se representan las estructuras de control del código a analizar. En estos diagramas cada nodo representa un conjunto de tareas de procesamiento, mientras que cada arista representa el flujo de control entre dos nodos. Cada sección de código representada como un diagrama de control de flujo debe tener un nodo de entrada y al menos un nodo de salida.
Cada estructura de control existente en un código puede representarse mediante nodos y artistas dentro del diagrama. La estructura if-else
, por ejemplo, será representada por un nodo inicial que contará (además de otras posibles tareas) con la condición que generará dos caminos diferentes de ejecución, aquellas tareas que serán ejecutadas dependiendo del camino elegido serán dos nodos más en el diagrama, los cuales llevaran a un nodo final que ejecutará las tareas comunes a ambos caminos.
La medida obtenida de este análisis representa la cantidad de pruebas mínimas necesarias para recorrer cada camino del diagrama de flujo de manera que se haya ejecutado y testeado cada sentencia presente en el programa.
A continuación se muestra un ejemplo de un código en Python muy sencillo y su representación en un diagrama de control de flujo.
import random
num = random.randint(1, 10)
print("Inicia el programa")
if num > 3:
for i in range(num):
print("Hola")
else:
if num == 2:
print("El número es 2")
print("El número es menor a 3")
print("Fin del programa")
En este ejemplo el nodo A representa las lineas 1-4 del código, que incluyen hasta la condición if
. El nodo B representa la linea 5, donde se realiza la iteración for
. El nodo E es la linea 6, que es la que se repite dentro del bucle for
. El nodo C incluye las lineas 7 y 8, donde se realiza otro if
.
Por otro lado, el nodo D representa la linea 9 y el nodo F la 10. G es el único final del sistema que representa la linea 11 ejecutando la última linea de código, por la que se pasa independientemente del camino elegido.
Cálculo de la complejidad ciclomática
La complejidad ciclomática puede describirse como el número de caminos diferentes que puede seguir la ejecución de un programa. Existen diversas formulas o formas de calcularlo:
Método 1
V(g) = E - N + 2
donde
E = cantidad de aristas
N = cantidad de nodos
Para el ejemplo anterior la complejidad ciclomática se calcularía:
V(g) = 9-7+2 = 4
Método 2
V(g) = P+1
donde
P = cantidad de nodos predicados o nodos que contienen condiciones
Para el ejemplo el valor de P es 3 ya que los únicos nodos que contienen condiciones son A,B y C:
V(g) = 3 + 1 = 4
Método 3
Otra forma de calcular este número es contar la cantidad de caminos posibles de ejecución que puede seguir el programa y sumar a ese resultado 1. En nuestro ejemplo solo existen 3 posibles caminos:
A -> B -> E -> G
A-> C-> D-> F-> G
A-> C-> F-> G
Por lo que al sumar 1 no daría nuevamente una complejidad de 4.
Aunque es posible calcular esta métrica para secciones de código sencillas, cuando se trabaja con programas más complejos o con gran cantidad de lineas será necesario utilizar alguna herramienta que realice este cálculo de manera automática.
Importancia para el testing
El valor de la complejidad ciclomática permite determinar la cantidad de casos de pruebas que serán necesarios para evaluar la totalidad de los posibles flujos del código. Esto asegura haber probado el código en su totalidad, y reduce la inestabilidad del software.
Existen diferentes estándares para evaluar un programa dependiendo esta métrica, y los valores pueden variar ligeramente según el caso o lenguaje de programación. A continuación se muestra una de ellas:
1-10 | Código bien estructurado Capacidad de testeo alta Costo y esfuerzo bajo |
10-20 | Código complejo Capacidad de testeo media Costo y esfuerzo medio |
20-40 | Código muy complejo Capacidad de testeo baja |
>40 | No testeable Costo y esfuerzo alto |
Reducir la complejidad ciclomática
Existen diferentes métodos y técnicas que pueden utilizarse durante y después del desarrollo para disminuir la complejidad ciclomática de un código y, de esta manera, reducir el costo de mantenimiento, testeo y modificación de un programa.
Durante el desarrollo de un software es una buena práctica obtener esta métrica para cada módulo que se esté creando. Si este número es más alto de lo recomendado (10 o 15 dependiendo del autor) es necesario dividir el módulo en módulos más pequeños.
Al escribir funciones es necesario tener en cuenta el principio de responsabilidad única. Cada función del software debe tener solo una responsabilidad, y todo el código que no forme parte del núcleo de dicha responsabilidad deberá ser extraído en una función propia. Esta práctica aumentará también la usabilidad del código, ya que cada función se encargará de hacer una tarea específica y evitará el código duplicado.
Finalmente es importante evitar lo más posible el uso excesivo de estructuras de control y parámetros opcionales en las funciones. Estos elementos crearán más bifurcaciones dentro del código y, por lo tanto, aumentarán su complejidad ciclomática. La práctica que más ayudará a eliminarlos es el uso de patrones de diseños en el código. Estos permitirán agregar diferentes comportamientos sin generar nuevos flujos.
En este artículo hemos visto una pequeña introducción a la métrica complejidad ciclomática , su importancia en el desarrollo y mantenimiento de software y como reducirla.
Espero que les sea de utilidad!