Une qualimétrie simple au service des développeurs
La mesure de la qualité du code permet de faciliter et de limiter les phases de refactoring. Le point sur les principales méthodes répondant à cette question, et présentation de quelques outils Open Source.
A quoi peut servir la qualimétrie ?
Un code de bonne qualité, simple, lisible est testable est nécessaire à la réussite d'un projet de développement ainsi qu'à l'efficacité et la bonne humeur des développeurs qui y participent. Malheureusement, maîtriser la qualité du code produit est loin d'être une tâche aisée : avec le temps et au fur et à mesure des modifications, le code a une tendance presque génétique à se complexifier. Il doit alors être remanié par une opération de "refactoring" et sa qualité doit être restaurée.
Ces modifications sont de nature purement techniques. Il ne s'agit pas d'ajouter de nouvelles fonctionnalités mais d'améliorer la lisibilité, la maintenabilité du code et la simplicité des tests. Il faut donc être capable d'identifier ce qui doit être remanié, de suivre la progression des travaux et la non-régression de la qualité tout en minimisant les efforts fournis. Un refactoring endémique peut, à l'extrême, compromettre le respect des délais de livraison.
Il existe de nombreuses métriques de la qualité logicielle issues des travaux de chercheurs et d'acteurs de l'industrie des logiciels. Parmi les articles de référence citons simplement ceux de McCabe sur la complexité du code, de Chidamber & Kemerer sur le couplage des objets et de Robert Martin sur l'analyse des dépendances.
Les métriques issues de ces travaux peuvent sembler particulièrement ésotériques pour les non initiés, difficilement utilisables dans le contexte quotidien d'un projet. Elles peuvent toutefois être d'une grande utilité aux développeurs et aux chefs de projets techniques pour pouvoir procéder rapidement à des réajustements du code. Leur mise en oeuvre n'est pas nécessairement complexe comme le montre les trois exemples suivants.
La complexité cyclomatique de McCabe
Derrière ce nom impressionnant, se cache une manière simple de calculer le niveau de complexité de l'algorithme d'une méthode et la difficulté à la tester exhaustivement. La complexité cyclomatique se mesure en comptant le nombre de boucles et de conditions dans le code, c'est-à-dire le nombre d'instruction "if", "while", "do", "for", "switch" et "case". Le nombre de "ET" et de "OU" peut également être ajouté pour une évaluation plus stricte.
La valeur obtenue correspond au nombre de chemins indépendants à parcourir pour traverser une méthode du début à la fin en couvrant tous les cas possibles. Elle représente également le nombre minimum de chemins à tester pour obtenir une couverture exhaustive. Plus la valeur obtenue est élevée, plus la méthode est complexe et difficile à comprendre.
Il est généralement considéré qu'une complexité inférieure ou égale à 7 correspond à une méthode simple et qu'une valeur de 10 est le maximum acceptable. Au-delà de ce seuil, un remaniement est sérieusement à envisager. Deux options s'offrent au développeur : la simplification de l'algorithme lorsque cela est possible ou l'extraction d'une partie du code vers des sous-méthodes appelées à partir de la méthode principale.
Le couplage entre objets
Cette mesure permet de déterminer l'indépendance d'une classe par rapport au reste du système. Elle porte le nom de "couplage efférent" (noté Ce) ou "Couplage between objects" (noté CBO). On recense, pour l'obtenir le nombre de types différents utilisés pour les attributs de la classe, les paramètres des méthodes et les variables locales. Plus la valeur est élevée, plus la classe évaluée dépend d'autres classes du projet.
Elle sera donc susceptible de devoir être modifiée en cas d'évolutions apportées à d'autres parties du code. Ceci dénote souvent soit une trop grande centralisation des traitements, soit un mélange des rôles au sein d'une même classe. Un diagramme de classes obtenu à partir du code ressemble habituellement à un plat de spaghettis ou à une scène de bataille intergalactique.
Le remaniement peut être assez complexe et impacter une partie de la conception. Il faut essayer de maintenir la valeur du couplage entre objets en dessous de 10 pour des systèmes simples, et elle ne doit jamais dépasser 20. Une classe ne respectant pas ce critère devra être scindée ou dans certains cas, une partie de son code déplacé vers une autre classe existante.
Dépendances cycliques de paquetage
La gestion des dépendances entre les paquetages, c'est-à-dire la manière dont les classes d'un paquetage référencent des classes à l'extérieur de celui-ci, est un point mis en avant avec justesse par la méthode eXtreme Programming. En basant le développement du système sur les paquetages stables, et en les utilisant comme interface entre un module et les autres sous-systèmes, il est possible de garantir la rapidité de propagation des modifications qui sont apportées en cours de développement.
La réutilisation est également ainsi favorisée. Les dépendances cycliques entre paquetage - le cas où un paquetage A référence un paquetage B qui référence un paquetage C qui référence à sont tour A - sont bien sur à proscrire. Les cycles de vie sont alors liés et tous ces éléments ne peuvent ni être utilisés ni être versionnés séparément. Une fois identifiées, ces dépendances doivent être brisées par des techniques de programmation classiques.
Il suffit parfois simplement de déplacer une des classes vers le paquetage où elle aurait toujours du être. Si cela est insuffisant ou impossible, il faut envisager de revoir la conception de la classe où même la décomposition en paquetage. En ce qui concerne les dépendances cycliques, mieux vaut prévenir que guérir.
Comment mesurer ces métriques
Il est bien évidemment impossible d'effectuer ses mesures manuellement, en particulier si l'on souhaite surveiller leur évolution. Quelque que soit le langage objet utilisé pour le projet, il existe une offre logicielle commerciale fournie, et souvent haut de gamme, qui permet de calculer ces métriques (et bien d'autres) et de générer des tableaux de bords très complets.
Dans le cas des projets Java, la communauté OpenSource a développé des outils simples d'utilisation et d'une grande efficacité à destination des développeurs. Ceux-ci se présentent sous la forme de plug-in pour Eclipse, de tâches ANT ou de modules pour Maven. On retiendra en particulier :
Metrics fournit plus de 20 mesures de la qualité du code. Des plus simples, tel le nombre de lignes de code, aux plus sophistiquées comme l'index de spécialisation de classe. Le plug-in Eclipse permet de paramétrer des seuils d'alerte sur le respect de valeurs minimales et maximales des métriques.
JDepend et son plug-in eclipse JDepend4Eclipse sont spécialisés, comme leur nom l'indique, dans l'analyse des dépendances et des couplages entre les paquetages.
PMD est très utilisé pour ses modules de vérification du respect des règles de développement classiques (taille du code, blocs catch vides, etc...). Il implémente également un ensemble de règles sur le couplage et les règles de design.
Signalons également XRadar, plus complexe à mettre en oeuvre, mais également plus complet car il embarque, entre autre, PMD, JDepend, JavaNCSS, et CheckStyle. Il permet de créer ses propres indicateurs que qualité et génère des rapports très pertinents ; exploitables par toute l'équipe projet.
Si la mise en place d'outils de mesure appropriés facilite la maîtrise de la qualité de code, il est important de comprendre que le respect de valeurs cibles pour les métriques expliquées plus haut n'est pas le garant absolu d'un logiciel bien conçu et réalisé.
Ces techniques permettent aux développeurs de mieux maîtriser les programmes au travers de points de contrôles simples et efficaces. Ils restent toutefois de simples outils dont il faut interpréter les résultats pour entreprendre les actions adéquates sur le code. Rien ne remplace l'intelligence et le savoir faire.