Les outils de gestion de la qualité d'un projet Java et leur intégration à Maven 2

Antonio Gomes Rodrigues

Cet article se focalisera sur les métriques de code Java données par un ensemble de plugins Maven2.

- Titre : Les outils de gestion de la qualité d'un projet Java et leur intégration à Maven 2

- Auteur : Antonio Gomes Rodrigues

- Parution : 30 décembre 2008

- Mise à jour : 1 juin 2020

- Licence : Copyright ® 2008 Antonio Gomes Rodrigues. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Introduction à Maven2

- III. Présentation des plugins Maven2

- III-A. Surefire

- III-A-1. Présentation

- III-A-2. Installation

- III-A-3. Rapport généré

- III-B. Cobertura

- III-B-1. Présentation

- III-B-2. Installation

- III-B-3. Rapport généré

- III-C. CheckStyle

- III-C-1. Présentation

- III-C-2. Installation

- III-C-3. Rapport généré

- III-D. PMD/CPD

- III-D-1. Présentation

- III-D-2. Installation

- III-D-3. Rapports générés

- III-E. Findbugs

- III-E-1. Présentation

- III-E-2. Installation

- III-E-3. Rapport généré

- III-F. JDepend

- III-F-1. Présentation

- III-F-2. Installation

- III-F-3. Rapport généré

- IV. Attention à ne pas activer toutes les règles

- V. Tableaux de bord

- V-A. QALab

- V-B. XRadar

- V-C. Sonar

- V-D. Maven Dashboard

- V-E. SonarJ

- VI. Intégration continue

- VII. Un code documenté

- VIII. Un code respectant les standards (de Sun, de son entreprise, de son projet…)

- IX. Un code sans bug

- X. Un code maintenable, évolutif et simple

- X-A. Duplication de code

- X-B. Complexité Cyclomatique (CCN)

- X-C. Nombre de lignes dans une classe/méthode/… (SLOC)

- X-D. Couplage entre objets (Ca, Ce)

- X-E. Code mort

- X-F. Depth of Inheritance Tree (DIT)

- XI. Un code performant

- XII. Un code testé

- XIII. Conclusion

- XIV. Références

- XV. Remerciements

- XVI. Annexe A : Génération du diagramme de classe

- XVII. Annexe B : Génération du schéma de la base de données

I - Introduction

Les projets étant de plus en plus complexes et les équipes de développement réparties partout dans le monde, il devient de plus en plus important d'en suivre la qualité afin de corriger les problèmes le plus tôt possible.
Car comme vous avez dû le lire, plus un bug est découvert tard dans le cycle de réalisation du logiciel, plus sa correction est coûteuse.

Une politique de qualité doit être mise en place et comporter au moins ces quatre étapes :

- définition d'un objectif ;

- choix d'une méthode ;

- outillage de cette méthode ;

- mise en place d'un processus de suivi de qualité.

Notre objectif sera de valider la définition de la qualité suivante.
On peut définir la qualité d'un code source par ces critères :

- un code documenté ;

- un code respectant les standards (de Sun, de son entreprise, de son projet…) ;

- un code sans bug ;

- un code maintenable, évolutif et simple ;

- un code performant ;

- un code testé.

Nous ne traiterons pas du choix des méthodes, mais je vous conseille d'aller jeter un œil sur les méthodes agiles (Scrum, XP…), TDD et Lean.
Le choix de la méthode est important, car l'outillage technique ne se substitue pas à la méthode choisie, il en est le complément.

L'outillage des tests revêt plusieurs aspects et vise plusieurs objectifs :

- réduction des charges de test ;

- réduction des délais de test ;

- amélioration de la couverture des tests ;

- amélioration de la flexibilité des tests ;

- meilleure appréciation de l'état du processus de test ;

- meilleure motivation des testeurs par l'automatisation des tâches ingrates ou répétitives.

En Java de nombreux outils permettent de suivre la qualité des sources.
Parmi eux, nous utiliserons les plugins Maven2.

II - Introduction à Maven2

Maven2 permet grâce à la définition du projet dans le fichier pom.xml de gérer tout le cycle de vie du projet (compilation, test, packaging, installation…).
Plus d'information sur https://matthieu-lux.developpez.com/tutoriels/java/maven/?page=lifecycle-goal#L3.2

La partie qui nous intéresse est la partie de reporting et se configure dans le fichier pom.xml, rubrique reporting.

pom.xml

<reporting>
...
</reporting>

L'étape reporting consiste à la création d'un site Internet à l'aide de la commande « mvn site ». Par défaut, un certain nombre de pages sont créées automatiquement par le plugin « project-info-reports ».

Plus d'information sur http://maven.apache.org/plugins/maven-project-info-reports-plugin/

Pour ajouter les plugins de qualités que nous allons étudier, il faut ajouter dans la partie reporting la liste des plugins que nous voulons utiliser

pom.xml

<reporting>
<plugins>
...
</plugins>
</reporting>

Le résultat se trouvera en dessous de la partie « project-info-reports ».

Étudions maintenant ces plugins.

III - Présentation des plugins Maven2

III-A - Surefire

III-A-1 - Présentation

Surefire permet d'avoir les rapports d'exécution de tests unitaires.
Il est compatible avec un certain nombre de frameworks de tests unitaires comme JUnit (3.8 et 4.x), TestNG.
Vous trouverez plus d'informations sur http://maven.apache.org/plugins/maven-surefire-plugin/

III-A-2 - Installation

Dans la partie reporting du fichier pom.xml ajoutez le plugin

pom.xml

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>2.4.3</version>
</plugin>

III-A-3 - Rapport généré

III-B - Cobertura

III-B-1 - Présentation

Cobertura permet de connaître la couverture du code par les tests unitaires.
Cette information est importante, car, même avec un nombre élevé de test unitaire, on n'a pas forcément une bonne couverture et certaines parties importantes peuvent se retrouver peu ou pas testées.

Vous trouverez plus d'informations sur http://cobertura.sourceforge.net/

III-B-2 - Installation

Dans la partie reporting du fichier pom.xml ajoutez le plugin

pom.xml

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>2.2</version>
</plugin>

III-B-3 - Rapport généré

III-C - CheckStyle

III-C-1 - Présentation

CheckStyle permet de contrôler le respect des conventions de codage et d'avoir quelques métriques.
Ce contrôle est fait avec 126 règles et cela va du plus simple comme la longueur d'une ligne à des choses plus compliquées comme la complexité cyclomatique d'une classe.

Le plugin Maven met à disposition quatre fichiers de règles :

- config/sun_checks.xml
Convention Sun Microsystems (par défaut),
http://java.sun.com/docs/codeconv/

- config/maven_checks.xml
Convention de l'équipe Maven,
http://maven.apache.org/guides/development/guide-m2-development.html#maven%20code%20style

- config/turbine_checks.xml
Convention du projet Apache Turbine,
http://turbine.apache.org/common/code-standards.html

- config/avalon_checks.xml
Convention du projet Apache Avalon.
Ce projet n'existe plus.

De nombreux fichiers de règles peuvent se trouver sur Internet permettant d'être plus ou moins souple avec le standard de codage.
Et si ces règles ne vous conviennent pas, vous pouvez créer les vôtres et/ou paramétrer celles qui existent déjà.

Vous trouverez plus d'informations sur http://checkstyle.sourceforge.net/
Et la description des règles sur http://checkstyle.sourceforge.net/availablechecks.html

III-C-2 - Installation

Dans la partie reporting du fichier pom.xml ajoutez le plugin

pom.xml

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.2</version>
<configuration>
<configLocation>config/sun_checks.xml</configLocation>
</configuration>
</plugin>

III-C-3 - Rapport généré

III-D - PMD/CPD

III-D-1 - Présentation

PMD va analyser le code afin de trouver des problèmes potentiels tels que :

- bugs possibles ;

- code mort ;

- code non optimal ;

- expressions trop compliquées ;

- problèmes de sécurité ;

- problèmes de couplage entre objet/package ;

- …

Règle

Définition

Basic JSF rules

Règles pour JSF.

Basic JSP rules

Règles pour JSP.

Basic Rules

Règles basiques listant un certain nombre de bonnes pratiques.

Braces Rules

Règles sur l'utilisation des parenthèses.

Clone Implementation Rules

Règles sur l'utilisation de la méthode clone().

Code Size Rules

Règles concernant la taille du code.

Controversial Rules

Règles considérées controversées.

Coupling Rules

Règles sur le couplage.

Design Rules

Règles sur l'architecture.

Finalizer Rules

Règles sur l'utilisation des finalizers.

Import Statement Rules

Règles sur la partie « import » du code.

J2EE Rules

Règles pour J2EE

JavaBean Rules

Règles sur l'utilisation des JavaBeans.

JUnit Rules

Règles sur l'utilisation de JUnit.

Jakarta Commons Logging Rules

Règles pour les Commons Logging.

Java Logging Rules

Règles pour l'utilisation des Logging.

Migration Rules

Règles pour la migration d'un JDK à une autre version.

Naming Rules

Règles sur la nomenclature des objets/packages/…

Optimization Rules

Règles pour la performance du code.

Strict Exception Rules

Règles sur l'utilisation des Exceptions.

String and StringBuffer Rules

Règles sur l'utilisation de String et StringBuffer.

Security Code Guidelines

Règles sur la sécurité http://java.sun.com/security/seccodeguide.html#gcg

Unused Code Rules

Règles pour la détection du code mort.

CPD va analyser le code afin de trouver les morceaux de code dupliqué.

Vous trouverez plus d'informations sur http://pmd.sourceforge.net/

III-D-2 - Installation

Dans la partie reporting du fichier pom.xml ajoutez le plugin

pom.xml

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>2.4<version>
<configuration>
<rulesets>
<ruleset>/rulesets/basic.xml</ruleset>
<ruleset>/rulesets/imports.xml</ruleset>
<ruleset>/rulesets/unusedcode.xml</ruleset>
<exclude name="UnusedPrivateField" />
<ruleset>/rulesets/finalizers.xml</ruleset>
</rulesets>
</configuration>
</plugin>

III-D-3 - Rapports générés

PMD

CPD

III-E - Findbugs

III-E-1 - Présentation

Findbugs va trouver les bugs potentiels en analysant le bytecode Java. Pour cela il s'appuie sur une notion de 'bug patterns'.

Ces bugs sont classés en plusieurs catégories :

- Correctness
Regroupe les bugs généraux. Par exemple les boucles infinies, mauvaises utilisations de equals()…

- Bad practice
Regroupe les mauvaises pratiques. Par exemple les problèmes d'Exception, de ressources non fermées, mauvaises utilisations de comparaison de chaîne de caractères…

- Performance
Regroupe les problèmes de performance. Par exemple la création d'objets inutiles.

- Multithreaded correctness
Regroupe les problèmes liés au code multithread.

- Internationalization
Regroupe les problèmes liés à l'internationalisation d'une application.

- Malicious code vulnerability
Regroupe les problèmes de vulnérabilité. Par exemple du code qui pourrait être détourné de son utilisation…

- Security
Regroupe les problèmes de sécurité. Par exemple les problèmes liés au protocole http, les SQL injections…

- Dodgy
Regroupe le « smell code ». Par exemple les comparaisons redondantes avec null, variables non utilisées…

Il existe un autre paquet de règles.
On peut le trouver au format jar sur http://fb-contrib.sourceforge.net/

Vous trouverez plus d'informations sur http://findbugs.sourceforge.net/factSheet.html

III-E-2 - Installation

Dans la partie reporting du fichier pom.xml ajoutez le plugin

pom.xml

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>1.0.0<version>
</plugin>

III-E-3 - Rapport généré

III-F - JDepend

III-F-1 - Présentation

JDepend analyse le code pour en calculer des métriques (l'extensibilité, la réutilisabilité et la maintenabilité des sources) sur la qualité du design pour les packages du projet.

Ces métriques sont :

- nombre de classes et d'interfaces (TC = CC + AC)
Le nombre de classes concrètes et abstraites (incluant les interfaces) dans un package est un indicateur de l'extensibilité d'un package.
-> Plus ce nombre est important, plus les entités qu'elles implémentent peuvent être étendues indépendamment les unes des autres ;

- couplage par dépendance ascendante (Ca ou Afferent Coupling)
Le nombre de packages tiers utilisant un package donné est un indicateur de la responsabilité d'un package. Cela permet de mettre en évidence qu'un package est au centre de l'application ou une mauvaise gestion des packages.
-> Ce nombre doit être le plus petit possible ;

- couplage par dépendance descendante (Ce ou Efferent Coupling)
Le nombre de packages tiers utilisés par un package donné est un indicateur d'indépendance du code.
-> Ce nombre doit être le plus petit possible ;

- degré d'abstraction (A ou Abstractness)
Le degré d'abstraction correspond au pourcentage entre le nombre de classes abstraites (plus les interfaces) dans le package analysé par rapport au nombre total de classes de ce package.
Cette métrique peut varier de 0 à 1. 0 indique qu'un package est concret, tandis que 1 indique un package abstrait.
-> Ce nombre doit être proche d'une des deux bornes ;

- instabilité (I ou Instability)
C'est le ratio I = Ce / (Ce + Ca). Cette métrique est un indicateur de stabilité par rapport à la mise jour d'autres packages.
Cette métrique peut varier de 0 à 1. 0 indique qu'un package est complètement stable, tandis que 1 indique un package complètement instable ;

- distance normale (D ou Distance from the main sequence)
Cette métrique correspond à la distance de la droite perpendiculaire à la droite idéale A+I=1. C'est un indicateur d'équilibre d'un package entre l'abstraction et l'instabilité.
Les packages supposés idéaux sont soit complètement abstraits et stables (A=0,I=1) soit complètement concrets et instables(A=0,I=1).
-> Ce nombre doit être proche de 0 :

- volatility (V)
La volatilité d'un package est sa tendance à évoluer au fil du temps.
-> 0 indique que le package est le centre de l'application.

Vous trouverez plus d'informations sur http://clarkware.com/software/JDepend.html

III-F-2 - Installation

Dans la partie reporting du fichier pom.xml ajoutez le plugin

pom.xml

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jdepend-maven-plugin</artifactId>
<version>2.0-beta-2<version>
</plugin>

III-F-3 - Rapport généré

IV - Attention à ne pas activer toutes les règles

À moins de vouloir décourager toute l'équipe (y compris vous), n'activez pas toutes les règles d'un coup.
Il vaut mieux choisir certaines règles à activer au début. Une fois ces règles validées (ou du moins un certain pourcentage atteint), ajouter d'autres règles et ainsi de suite.
Je préconise d'activer les règles sur les Exceptions et de la gestion des traces (log) dès le début.

Pour sélectionner les règles à utiliser pour chaque plugin, voir les parties en gras dans la partie précédente afin de bien configurer chaque plugin.

Sur http://wiki.netbeans.org/ActionPMD vous trouverez un exemple de règles que le projet Netbeans exclut lors de son utilisation de PMD.

Ces plugins Maven2 existent aussi au format plugin pour la majorité des IDE.
Il est utile d'installer ces plugins sur chaque IDE des postes de développement.

Cela a plusieurs avantages :

- intégration aux IDE et donc une utilisation plus simple ;

- responsabilisation des développeurs aux problèmes de qualité ;

- les développeurs vérifient leur code avant de faire un commit.

V - Tableaux de bord

Une fois Maven2 mis en place avec tout ses plugins, il est judicieux d'avoir un tableau de bord regroupant le maximum d'informations utiles en une seule page.
Pour cela il existe un certain nombre de plugins Maven2 qui permettent de créer ce tableau de bord.

V-A - QALab

Outil intéressant, mais semble ne plus être maintenu.
Pour plus d'informations : http://qalab.sourceforge.net/index.html

V-B - XRadar

Outil intéressant, mais j'attends qu'il prenne en charge FindBugs.
Pour plus d'informations : http://xradar.sourceforge.net/

V-C - Sonar

Semble prometteur, vivement que la version 1.5 sorte afin qu'il prenne en compte plus de métriques et le support de plugins externes comme FindBugs, JDepend.
Site officiel : http://sonar.codehaus.org/
Pour plus d'informations : https://linsolas.developpez.com/articles/java/qualite/sonar/

V-D - Maven Dashboard

Maven Dashboard permet de centraliser un certain nombre de rapports créés par Maven. De plus il y a une option pour historiser les statistiques.
Pour plus d'informations : http://mojo.codehaus.org/dashboard-maven-plugin/

V-E - SonarJ

Permet d'avoir un contrôle plus poussé sur l'architecture du projet en définissant les droits de communication entre chaque couche.
Pour plus d'informations : http://www.hello2morrow.com/products/sonarj

VI - Intégration continue

Tout est en place. Il reste juste un dernier détail : on est obligé de lancer manuellement la commande « mvn site » pour créer les rapports.
La solution à ce problème est de mettre en place l'intégration continue sur votre projet.

Le principe est d'exécuter les étapes (goals) de votre fichier pom.xml de Maven2 automatiquement.
Les déclencheurs de cette exécution peuvent être :

- à date fixe (toutes les nuits) ;

- après un commit ;

- à la demande ;

- …

Plus d'informations sur : https://conception.developpez.com/cours/?page=tools-cat#ci

Maintenant que tous les outils ont été présentés et installés, nous allons valider la définition de la qualité définie en début de l'article.

VII - Un code documenté

Outil utilisé : CheckStyle

Les règles concernant le contrôle de la JavaDoc se trouvent à l'adresse http://checkstyle.sourceforge.net/config_javadoc.html

Voilà les recommandations de Sun pour bien écrire ces JavaDoc http://java.sun.com/j2se/javadoc/writingdoccomments/index.html

L'écriture et la maintenance de la JavaDoc pouvant devenir facilement chronophages, il est inutile d'avoir une couverture de 100 %, en particulier si les noms des paramètres/classes/… sont bien choisis.
Par exemple, les getters ou setters d'une classe.

Pour éviter d'écrire toute la documentation, il est possible de générer une partie de la documentation UML et du schéma de la base de données par rétro documentation (voir les annexes A et B).

VIII - Un code respectant les standards (de Sun, de son entreprise, de son projet…)

Outil utilisé : CheckStyle

Il y a un fichier de configuration livré avec CheckStyle reprenant les conventions Sun.
De plus on peut facilement ajouter d'autres conventions.
Il est conseillé de configurer sur chaque poste votre IDE pour qu'il puisse automatiquement formater le code source afin de respecter ces standards.

IX - Un code sans bug

Outils utilisés : CheckStyle, PMD, Findbugs

Avant de corriger un bug, bien penser à écrire le test unitaire associé s'il n'existe pas.
Pour cette partie, il faut faire attention aux faux positifs (l'outil va détecter un bug qui n'en est pas). Une fois cette vérification faite, la plupart du temps il suffit de corriger le bug comme indiqué par le plugin.

Voici quelques bugs trouvés avec FindBugs sur divers projets open source.

1. [RV] La méthode ignore une valeur de retour [RV_RETURN_VALUE_IGNORED]

value.replaceAll("/", "//");

Solution possible

value = value.replaceAll("/", "//");

2. [RpC] Repeated conditional tests [RpC_REPEATED_CONDITIONAL_TEST]

if (vertices[i].z == Float.MAX_VALUE || vertices[i].z == Float.MAX_VALUE)
ptXY += i + 1;

Solution possible

if (vertices[i].z == Float.MAX_VALUE) {
ptXY += i + 1;
}

3. [FE] Test d'égalité avec NaN erroné [FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER]

if (translucentLevel != Float.NaN)

Solution possible

if (!Float.isNaN (translucentLevel))

4. [OBL] Method may fail to clean up stream or resource [OBL_UNSATISFIED_OBLIGATION]

static void writeFile(String fileName, String text) {
try {
FileOutputStream os = new FileOutputStream(fileName);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os), 8192);
bw.write(text);
bw.close();
os = null;
} catch (IOException e) {
Logger.error("IO Exception: " + e.toString());
}
}

Solution possible

static void writeFile(String fileName, String text) {
try {
FileOutputStream os = new FileOutputStream(fileName);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os), 8192);
bw.write(text);
} catch (IOException e) {
Logger.error("IO Exception: " + e.toString());
}
finally {
if (bw != null){
bw.close();
}
if (os != null){
os.close();
}
}
}

5. [DB] Méthode utilisant le même code pour deux branches [DB_DUPLICATE_BRANCHES]

if (this instanceof TraceRenderer) {
madEnd = madBeg;
} else {
madEnd = madBeg;
}
}

Solution possible

if (this instanceof TraceRenderer) {
madEnd = madBeg;
} else {
// madEnd = autre chose
}
}

6. [RCN] Test de nullité d'une valeur préalablement déréférencée [RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE]

public Object getResult() {
Element e = params.get(0).getValue();
Node n = e.getFirstChild();
return (e == null) ? e.getTextContent() : n.getTextContent();
}

Solution possible
Le problème vient de la ligne suivante qui déclenche un NullPointerException si e == null

Node n = e.getFirstChild();

Et donc le test de la ligne suivante ne sert à rien

return (e == null) ? e.getTextContent() : n.getTextContent();

7. [EC] Appel de equals() avec un argument à null [EC_NULL_ARG]

if (object.equals(null))

Solution possible

if (object == null)

8. [DMI] Chemin absolu codé en dur dans le code [DMI_HARDCODED_ABSOLUTE_FILENAME]

final File dir = new File("C:\\temp\\test.txt");

9. [NP] Déréférencement d'un pointeur null dans le chemin d'exception d'une méthode [NP_ALWAYS_NULL_EXCEPTION]

public final void start() {
UIComponentFactory componentFactory = null;
try {
componentFactory = UIComponentFactory.createInstance(true);
}
catch (MalformedURLException e) {
e.printStackTrace();
componentFactory.getClass();
}

Solution possible

public final void start() {
UIComponentFactory componentFactory = null;
try {
componentFactory = UIComponentFactory.createInstance(true);
}
catch (MalformedURLException e) {
e.printStackTrace();
}

X - Un code maintenable, évolutif et simple

Outils utilisés : CheckStyle, PMD, Findbugs, JDepend, CPD

Comme pour la partie précédente, l'écriture de tests unitaires est fortement conseillée.
Voici les principales métriques à regarder.

X-A - Duplication de code

Outils utilisés : CheckStyle et CPD
Solutions : Extract Method, Pull Up Method, Form Template Method, Substitute Algorithm

Le gros problème du code dupliqué est que s'il y a un bug, il faut le corriger à tous les endroits où il a été dupliqué. Sans parler de l'augmentation du nombre de lignes de code dans le programme.

Il existe plusieurs solutions à ce problème, comme :

- créer une méthode et y faire appel (Extract Method) ;

- mettre le code dans la classe parente (Pull Up Method) ;

- changer d'algorithme ;

- etc.

X-B - Complexité Cyclomatique (CCN)

Outils utilisés : CheckStyle et PMD
Solutions : Extract Method, Replace Conditional with Polymorphism Design Patterns : Strategy, Factory

La complexité cyclomatique représente le nombre de chemins indépendants à parcourir pour traverser une méthode du début à la fin en couvrant tous les cas possibles. Au-dessus d'une valeur de 10, le code devient complexe à lire et il faut énormément de tests unitaires.

Il existe plusieurs solutions à ce problème, comme :

- créer une méthode et y faire appel (Extract Method) ;

- utiliser le polymorphisme (Strategy…) ;

- utiliser des Factory ;

- etc.

X-C - Nombre de lignes dans une classe/méthode/… (SLOC)

Outils utilisés : CheckStyle et PMD
Solutions : Extract Method, Replace Temp with Query, Introduce Parameter Object, Preserve Whole Object, Replace Method with Method Object.
Design Patterns : Strategy.

Des classes/méthodes/… trop longues sont difficile à lire et peuvent être le résultat d'une classe/méthode ayant trop de responsabilités.

Il existe plusieurs solutions à ce problème, comme :

- créer une méthode et y faire appel (Extract Method) ;

- utiliser le polymorphisme (Strategy…) ;

- transformer une méthode en objet ;

- etc.

X-D - Couplage entre objets (Ca, Ce)

Outils utilisés : CheckStyle, JDepend
Solutions : Move Method, Extract Class
Design Patterns : Strategy, Factory.

Cette mesure permet de déterminer l'indépendance d'une classe par rapport au reste du système. Elle est donc susceptible d'être modifiée si le reste du système change.

Il existe plusieurs solutions à ce problème, comme :

- déplacer la méthode qui est l'origine du couplage (Move Method) ;

- utiliser le polymorphisme (Strategy…) ;

- créer une classe à partir de la classe existante (Extract Class) ;

- utiliser des Factory ;

- etc.

X-E - Code mort

Outils utilisés : PMD
Solutions : Supprimer le code

PMD permet de détecter le code mort (code qui n'est plus utilisé). Il suffit de le supprimer.

X-F - Depth of Inheritance Tree (DIT)

Outils utilisés : PMD
Solutions : Replace Inheritance with Delegation, Pull Up/Push Down Method
Design Patterns : Delegation

Cette mesure permet de déterminer la profondeur de la classe dans l'arbre d'héritage.
Une valeur maximum de 5 est conseillée.

Il existe plusieurs solutions à ce problème, comme :

- mettre le code dans la classe parente (Pull Up Method) ;

- mettre le code dans la classe fille (Push Down Method) ;

- utiliser la Delegation ;

- etc.

Pour la liste des solutions (refactoring), allez sur http://www.refactoring.com/catalog/index.html

XI - Un code performant

Outils utilisés : PMD, Findbugs

Pour ce critère, le seul moyen fiable est d'utiliser un profiler et un outil de mesure (JMeter…) afin d'avoir des mesures concrètes.
Les résultats permettront de détecter les problèmes s'ils existent.

Des solutions sans modifier le code source existent :

- utiliser une version plus récente de la JVM ;

- optimiser / Paramétrer la JVM ;

- optimiser / Paramétrer la base de données ;

- optimiser / Paramétrer le serveur d'application.

Plusieurs bons articles sur les performances des applications Java EE se trouvent sur le blog de Xebia.

De même Sun met à disposition des documentations sur le sujet http://java.sun.com/performance/reference/whitepapers/tuning.html

La catégorie « Optimization Rules » de règles de PMD, ainsi que les catégories « Multithreaded correctness » et « Performance » de FindBugs pourront aider dans une moindre mesure.

XII - Un code testé

Outils utilisés : SureFire, Cobertura

Avec ces deux plugins on pourra connaître :

- le taux d'échec des tests unitaires ;

- la couverture de ces tests unitaires.

Ces deux métriques doivent être lues ensemble.
Il faut faire attention au fait que les tests unitaires sont du code à maintenir/refactorer/…
Et donc comme pour la documentation, une couverture de 100 % est généralement illusoire et inatteignable.
Un outil qui permet de connaître la difficulté d'écriture des tests unitaires se trouve sur http://code.google.com/p/testability-explorer/
De même pour détecter les singletons http://code.google.com/p/google-singleton-detector/

Penser à utiliser les extensions de JUnit (DBUnit…), les mocks (EasyMock, JMock… permettant de simuler un objet sans instancier cet objet, ce qui permet de ne tester qu'une chose à la fois) et la programmation orientée aspect pour vous simplifier l'écriture des tests unitaires.

Pour la couche métier, FitNesse est très intéressant (un plugin pour Maven2 existe et sera traité dans un prochain article). Cet outil se présente sous la forme d'un wiki avec des tests intégrés exécutables sous la forme de tableaux (valeurs d'entrées et résultat attendus…). L'avantage est que ces tableaux peuvent être remplis par un non-informaticien, par exemple l'utilisateur de l'application qui connait la partie métier.
Pour plus d'informations, http://www.fitnesse.org/

Un taux d'échec de 0 % et une bonne couverture de ces tests demanderont quand même de regarder manuellement le code des tests unitaires pour valider qu'ils sont bien écrits.

Un test unitaire bien écrit doit répondre aux critères suivants :

- être rapide ;

- s'exécuter en isolation (ne tester qu'une partie) ;

- être fragile (échouer si le comportement du code testé change) ;

- tester les conditions limites (par exemple tester avec une valeur null) ;

- être indépendants les uns des autres ;

- …

Voilà un article regroupant un certain nombre de mauvaises pratiques de tests unitaire https://bruno-orsier.developpez.com/antipatterns/james-carr/

Il ne faut pas oublier un certain nombre de tests (tests d'intégration, test de l'interface graphique…) qui ne rentrent pas dans le calcul de couverture de Cobertura et qui sont quand même importants.

XIII - Conclusion

L'outillage vient en plus d'une politique globale de qualité, et seulement après la définition d'une méthode de développement axé sur la qualité, et non pour la remplacer.
Comme nous l'avons vu, il existe un certain nombre d'outils permettant de contrôler la qualité d'un projet. De plus, ces outils existent aussi en plugin pour votre IDE. Malheureusement ces outils ne sont pas suffisants pour tout contrôler et donc il y a quand même une partie manuelle à réaliser.
De plus, il est important de comprendre que de bonnes métriques ne sont pas le garant absolu d'un logiciel bien conçu et réalisé.
Malgré ça, ces mesures permettent de nous orienter pour le reste de l'audit.
Avec un peu d'expérience (utilisation de ces outils et une liste de contrôles manuels à faire) et la loi de Pareto (aussi appelée loi des 80/20), cela sera suffisant pour la majorité des cas.

XIV - Références

Articles et blog sur les méthodes agiles :
https://lagace.developpez.com/extreme-programming/
https://medina.developpez.com/cours/extreme-programming/
http://www.qualitystreet.fr/
http://scrum.aubryconseil.com/

Maven2 :
site officiel
https://dcabasson.developpez.com/articles/java/maven/presentation-maven2/
https://dcabasson.developpez.com/articles/java/maven/introduction-maven2/

XV - Remerciements

Merci à Romain Linsolas, Ricky81, regis1512 pour leurs relectures et leurs conseils.

XVI - Annexe A : Génération du diagramme de classe

Un dessin valant mieux que du texte, avoir un diagramme UML d'une classe peut aider à une meilleure compréhension. Avec l'aide de Maven2 et d'UMLGraph, on peut facilement ajouter des diagrammes UML dans la javadoc.
Le principe est que à partir du code et de certaine balise Javadoc, il va faire du reverse engineering afin de créer ces diagrammes.

Puis dans le pom.xml, modifier/créer la partie reporting

pom.xml

<project...>
...
<reporting>
<plugins>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<source>1.5</source>
<doclet>
gr.spinellis.umlgraph.doclet.UmlGraphDoc
</doclet>
<docletArtifact>
<groupId>gr.spinellis</groupId>
<artifactId>UmlGraph</artifactId>
<version>4.4</version>
</docletArtifact>
<additionalparam>
-inferrel -inferdep -quiet -hide java.*
-collpackages java.util.* -qualify
-postfixpackage -nodefontsize 9
-nodefontpackagesize 7 -attributes -operations
-enumerations -enumconstants -visibility
-link "http://java.sun.com/j2se/1.5/docs/api/"
-link "http://static.springframework.org/spring/docs/2.0.x/api/"
-link "http://www.hibernate.org/hib_docs/v3/api/"
</additionalparam>
</configuration>
</plugin>
</plugins>
</reporting>
...
</project>

Voilà un exemple de ce que l'on peut obtenir

URL d'UMLGraph : http://www.umlgraph.org/

XVII - Annexe B : Génération du schéma de la base de données

De nombreux outils permettent de faire du reverse engineering sur une base de données afin d'avoir le schéma de celle-ci. Parmi ces outils SchemaSpy a l'avantage d'avoir un plugin pour Maven2.

La première chose à faire est d'ajouter le login/password dans votre fichier setting.xml

setting.xml

<profiles>
…
<profile>
<id>development</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<schemaspy.username>antonio</schemaspy.username>
<schemaspy.password></schemaspy.password>
</properties>
</profile>
…
</profiles>

Puis d'ajouter le Wakaleo repository (dans le fichier pom.xml)

pom.xml

<pluginRepositories>
...
<pluginRepository>
<id>Wakaleo Repository</id>
<url>http://maven.wakaleo.com/repos/</url>
</pluginRepository>
...
</pluginRepositories>

Et enfin

pom.xml

<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>com.wakaleo.schemaspy</groupId>
<artifactId>maven-schemaspy-plugin</artifactId>
<version>1.0</version>
</plugin>
...
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>com.wakaleo.schemaspy</groupId>
<artifactId>maven-schemaspy-plugin</artifactId>
<version>1.0</version>
</plugin>
...
</plugins>
</build>
...
<reporting>
<plugins>

<plugin>
<groupId>com.wakaleo.schemaspy</groupId>
<artifactId>maven-schemaspy-plugin</artifactId>
<version>1.0</version>
<configuration>
<databaseType>mysql</databaseType>
<database>antonio</database>
<host>localhost</host>
<user>${schemaspy.username}</user>
<password>${schemaspy.password}</password>
</configuration>
</plugin>
</plugins>
</reporting>

La documentation générée se trouve dans le répertoire target/site/schemaspy et sera composée de plusieurs onglets.

URL du plugin : http://maven.wakaleo.com/mojo/maven-schemaspy-plugin/index.html