La complexité cyclomatique

La complexité cyclomatique d’une fonction est le nombre de régions visibles dans son logigramme. Cet indicateur a été introduit en 1976 par Thomas McCabe. Il sert notamment à évaluer le nombre de tests à effectuer pour tester unitairement une fonction. Et plus une fonction a une complexité cyclomatique élevée, moins elle est facile à maintenir.

Cas trivial

Prenons le cas trivial d’une fonction qui ne fait rien. Sa complexité cyclomatique est de 1. En effet, si on dessine son logigramme, le nombre de régions délimitées est de 1.

void fonctionTriviale() {
    // je ne fais rien
}
Logigramme d'une fonction triviale

Autre exemple

Logigramme d'une fonction de complexité cyclique 4

Problèmes en cas de grande complexité cyclomatique

Si une fonction a une grande complexité cyclomatique, on peut s’attendre aux problèmes suivants:

  • par construction, il va falloir de nombreux tests pour la valider intégralement. C’est pourquoi d’ailleurs de telles fonctions sont rarement testées unitairement par les développeurs.
  • la fonction est un nid à bug. En effet, il y a tellement de cas, de branchements de code, qu’un développeur devant apporter une modification dans la fonction verra difficilement tous les endroits où il doit agir. D’autre part, il devra anticiper les conséquences de ses changements dans toutes les branches conditionnelles de la fonction. Cela fait de nombreux cas à traiter
  • le risque accru de fuites mémoire ou plus généralement, de non libération des ressources. En effet plus la complexité cyclomatique est grande, plus il y de branches dans une fonction. Donc si une ressource locale (mémoire, fichier, etc. ) est allouée dans une branche, il faut penser à la libérer dans toutes les branches suivantes menant à la sortie de la fonction

La norme en C++ est de dire qu’au delà d’une complexité cyclomatique de 10, la fonction est trop compliquée, il faut la simplifier.

Faiblesse de l’indice

La limite de la complexité cyclomatique et qu’elle ne permet pas de dire si une fonction va être facilement compréhensible par un développeur. Parce que après tout, si une fonction donne de bons résultats, le fait qu’elle ait de nombreuses branches conditionnelles ne pose pas de problème au processeur, qui l’exécute.

Par contre, quand il faut la modifier, la chose importante est avant tout la capacité du développeur à la comprendre. Or l’éventuelle difficulté à comprendre ne dépend pas forcément du nombre de branches. Pour s’en assurer prennons l’exemple du switch case.

Exemple du switch

switch (valeurEntiere) {
  case 1: 
    // fais ceci
    break;
  case 2:
    // fais cela
    break;
  case 3:
  default:
    // fais ce qu'il te plait
    break;
}

C’est pourquoi notre partenaire SONAR a développé un autre indice, appelé complexité cognitive, pour évaluer la difficulté de compréhension d’une fonction.