Développons en Java 2.30 | |
Copyright (C) 1999-2022 Jean-Michel DOUDOUX | (date de publication : 15/06/2022) |
|
Niveau : | Elémentaire |
Le but de ce chapitre est de proposer un ensemble de conventions et de règles pour faciliter la compréhension et donc la maintenance du code.
Ces règles ne sont pas à suivre explicitement à la lettre : elles sont uniquement présentées pour inciter les développeurs à définir et à utiliser des règles dans la réalisation du code surtout dans le cadre d'un travail en équipe. Les règles proposées sont celles couramment utilisées. Il n'existe cependant pas de règle absolue et chacun pourra utiliser tout ou partie des règles proposées.
La définition de conventions et de règles est importante pour plusieurs raisons :
Le contenu de ce document est largement inspiré par les conventions de codage historiquement proposées par Sun.
Ce chapitre contient plusieurs sections :
Java utilise des fichiers pour stocker les sources et le bytecode des classes.
Les packages permettent de grouper les classes sous une forme hiérarchisée. Le choix des critères de regroupement est laissé aux développeurs.
Il est préférable de regrouper les classes par packages selon des critères fonctionnels.
Les fichiers inclus dans un package doivent être insérer dans une arborescence de répertoires équivalente.
Chaque fichier source ne doit contenir qu'une seule classe ou interface publique. Le nom du fichier doit être identique au nom de cette classe ou interface publique en respectant la casse.
Il faut éviter d'utiliser dans ce nom des caractères accentués qui ne sont pas toujours utilisables par tous les systèmes d'exploitation.
Les fichiers sources ont pour extension .java car le compilateur javac fourni avec le J.D.K. utilise cette extension.
Exemple : |
javac MaClasse.java |
Les fichiers binaires contenant le bytecode ont pour extension .class car le compilateur génère un fichier avec cette extension à partir du fichier source .java correspondant. De plus, elle est obligatoire pour l'interpréteur Java qui l'ajoute automatiquement au nom du fichier fourni en paramètre.
Exemple : |
java MaClasse |
Un fichier ne devrait pas contenir plus de 2 000 lignes de code.
Des interfaces ou classes privées ayant une relation avec la classe publique peuvent être rassemblées dans un même fichier. Dans ce cas, la classe publique doit être la première dans le fichier.
Chaque fichier source devrait contenir dans l'ordre :
Chaque fichier source devrait commencer par un commentaire multiligne contenant au minimum des informations sur le nom de la classe, la version, la date, éventuellement le copyright et tous les autres commentaires utiles :
Exemple : |
/*
* Nom de classe : MaClasse
*
* Description : description de la classe et de son rôle
*
* Version : 1.0
*
* Date : 23/02/2001
*
* Copyright : moi
*/ |
La première ligne de code du fichier devrait être une clause package indiquant à quel paquetage appartient la classe. Le fichier source doit obligatoirement être inclus dans une arborescence correspondante au nom du package.
Il faut indiquer ensuite l'ensemble des paquetages à importer : ceux dont les classes vont être utilisées dans le code.
Exemple : |
package monpackage;
import java.util.*;
import java.text.*; |
Les différents éléments qui composent la définition de la classe ou de l'interface devraient être indiqués dans l'ordre suivant :
Il existe deux types de commentaires en Java :
Les commentaires ne doivent pas être entourés par de grands cadres dessinés avec des étoiles ou d'autres caractères.
Les commentaires ne devraient pas contenir de caractères spéciaux tels que le saut de page.
Les commentaires de documentation utilisent une syntaxe particulière utilisée par l'outil javadoc pour produire une documentation standardisée des classes et interfaces au format HTML. La documentation de l'API du J.D.K. est le résultat de l'utilisation de cet outil de documentation
Cette documentation concerne les classes, les interfaces, les constructeurs, les méthodes et les champs.
La documentation est définie entre les caractères /** et */ selon le format suivant :
Exemple : |
/**
* Description de la methode
*/
public void maMethode() { |
La première ligne de commentaires ne doit contenir que /**
Les lignes de commentaires suivantes doivent obligatoirement commencer par un espace et une étoile. Toutes les premières étoiles doivent être alignées.
La dernière ligne de commentaires ne doit contenir que */ précédé d'un espace.
Un tel commentaire doit être défini pour chaque entité : une classe, une interface et chaque membre (variables et méthodes).
Javadoc définit un certain nombre de tags qu'il est possible d'utiliser pour apporter des précisions sur plusieurs informations.
Ces tags permettent de définir des caractéristiques normalisées. Il est possible d'inclure dans les commentaires des tags HTML de mise en forme (PRE, TT, EM ...) mais il n'est pas recommandé d'utiliser des tags HTML de structure tels que Hn, HR, TABLE ... qui sont utilisés par javadoc pour formater la documentation.
Il faut obligatoirement faire précéder l'entité documentée par son commentaire car l'outil associe la documentation à la déclaration de l'entité qui le suit.
Pour les classes ou interfaces, javadoc définit les tags suivants : @see, @version et @author.
Exemple : |
/**
* description de la classe.
* explication supplémentaire si nécessaire
*
* @version 1.0
*
* @see UneAutreClasse
* @author Jean Michel D.
*/ |
Pour les méthodes, javadoc définit les tags suivants : @see, @param, @return, @exception et @author
Exemple : |
/**
* description de la méthode.
* explication supplémentaire si nécessaire
*
* @return description de la valeur de retour
* @param arg1 description du 1er argument
* : : :
* @param argN description du Neme argument
* @exception Exception1 description de la première exception
* : : :
* @exception ExceptionN description de la Neme exception
*
* @see UneAutreClasse#UneAutreMethode
* @author Jean Dupond
*/ |
Remarques :
Ces commentaires doivent ajouter du sens et des précisions au code : ils ne doivent pas reprendre ce que le code exprime mais expliquer clairement son rôle.
Tous les commentaires utiles à une meilleure compréhension du code et non inclus dans les commentaires de documentation seront insérés avec des commentaires de traitements. Il existe plusieurs styles de commentaires :
Il est conseillé de mettre un espace après le délimiteur de début de commentaires et avant le délimiteur de fin de commentaires lorsqu'il y en a un, afin d'améliorer sa lisibilité.
Ces commentaires sont définis entre les caractères /* et */ sur une même ligne
Exemple : |
if (i < 10) {
/* commentaires utiles au code */
...
} |
Ce type de commentaires doit être précédé d'une ligne blanche et doit suivre le niveau d'indentation courant.
Ce type de commentaires peut apparaître sur la ligne de code qu'elle commente mais il faut inclure un espace conséquent qui permette de séparer le code et le commentaire.
Exemple : |
i++; /* commentaires utiles au code */ |
Si plusieurs lignes qui se suivent contiennent chacune un tel commentaire, il faut les aligner :
Exemple : |
i++; /* commentaires utiles au code */
j++; /* second commentaires utiles au code */ |
Exemple : |
/*
* Commentaires utiles au code
*/ |
Ce type de commentaires doit être précédé d'une ligne blanche et doit suivre le niveau d'indentation courant.
Ce type de commentaire peut délimiter un commentaire sur une ligne complète ou une fin de ligne.
Exemple : |
i++; // commentaires utiles au code |
Ce type de commentaires peut apparaître sur la ligne de code qu'elle commente mais il faut inclure un espace conséquent qui permette de séparer le code et le commentaire.
Si plusieurs lignes qui se suivent contiennent chacune un tel commentaire, il faut les aligner :
Exemple : |
i++; // commentaires utiles au code
j++; // second commentaires utiles au code |
L'usage de cette forme de commentaires est fortement recommandé car il est possible d'inclure celui-ci dans un autre de la forme /* */ et ainsi mettre en commentaire un morceau de code incluant déjà des commentaires.
Il n'est pas recommandé d'utiliser des caractères accentués dans les identifiants de variables, cela peut éventuellement poser des problèmes dans le cas où le code est édité sur des systèmes d'exploitation qui ne les gèrent pas correctement.
Il ne doit y avoir qu'une seule déclaration d'entité par ligne.
Exemple : |
String nom;
String prenom; |
Cet exemple est préférable à
Exemple : |
String nom, prenom; //ce type de déclaration n'est pas recommandé |
Il faut éviter de déclarer des variables de types différents sur la même ligne même si cela est accepté par le compilateur.
Exemple : |
int age, notes[]; // ce type de déclaration est à éviter |
Il est préférable d'aligner le type, l'identifiant de l'objet et les commentaires si plusieurs déclarations se suivent pour retrouver plus facilement les divers éléments.
Exemple : |
String nom //nom de l'eleve
String prenom //prenom de l'eleve
int notes[] //notes de l'eleve |
Il est fortement recommandé d'initialiser les variables au moment de leur déclaration.
Il est préférable de rassembler toutes les déclarations d'un bloc au début de ce bloc. (un bloc est un morceau de code entouré par des accolades).
La seule exception concerne la déclaration de la variable utilisée comme index dans une boucle.
Exemple : |
for (int i = 0 ; i < 9 ; i++) { ... } |
Il faut proscrire la déclaration d'une variable qui masque une variable définie dans un bloc parent afin de ne pas complexifier inutilement le code.
Exemple : |
int taille;
...
void maMethode() {
int taille;
} |
Il ne doit pas y avoir d'espaces entre le nom d'une méthode et sa parenthèse ouvrante.
L'accolade ouvrante qui définit le début du bloc de code doit être à la fin de la ligne de déclaration.
L'accolade fermante doit être sur une ligne séparée dont le niveau d'indentation correspond à celui de la déclaration.
Une exception tolérée concerne un bloc de code vide : dans ce cas les deux accolades peuvent être sur la même ligne.
La déclaration d'une méthode est précédée d'une ligne blanche.
Exemple : |
class MaClasse extends MaClasseMere {
String nom;
String prenom;
MaClasse(String nom, String prenom) {
this.nom = nom;
this.prenom = prenom;
}
void neRienFaire() {}
} |
Il faut éviter d'écrire des méthodes longues et compliquées : le traitement réalisé par une méthode doit être simple et fonctionnel. Cela permet d'écrire des méthodes réutilisables dans la classe et facilite la maintenance. Cela permet aussi d'éviter la redondance de code.
Java propose deux syntaxes pour déclarer une méthode qui retourne un tableau : la première syntaxe est préférable.
Exemple : |
public int[] notes() { // utiliser cette forme
public int notes()[] { |
Il est fortement recommandé de toujours initialiser les variables locales d'une méthode lors de leur déclaration car contrairement aux variables d'instances, elles ne sont pas implicitement initialisées avec une valeur par défaut selon leur type.
Elle suit les mêmes règles que celles utilisées pour les méthodes.
Il est préférable de définir explicitement le constructeur par défaut (le constructeur sans paramètre). Soit le constructeur par défaut est fourni par le compilateur et dans ce cas il serait préférable de le définir soit il existe d'autres constructeurs et dans ce cas le compilateur ne définit pas de constructeur par défaut.
Il est préférable de toujours initialiser les variables d'instance dans un constructeur soit avec les valeurs fournies en paramètres du constructeur soit avec des valeurs par défaut.
Exemple : |
class Personne {
String nom;
String prenom;
int age;
Personne() {
this( "Inconnu", "inconnu", -1 );
}
Personne( String nom, String prenom, int age ) {
this.name = nom;
this.address = prenom;
this.age = age;
}
} |
Il est possible d'appeler un constructeur dans un autre constructeur pour faciliter l'écriture.
Il est recommandé de toujours appeler explicitement le constructeur hérité lors de la redéfinition d'un constructeur dans une classe fille grâce à l'utilisation du mot clé super.
Exemple : |
class Employe extends Personne {
int matricule;
Employee() {
super();
matricule = -1;
}
Employee(String nom, String prenom, int age, int matricule) {
super(nom, prenom, age);
this.matricule = matricule;
}
} |
Il est conseillé de ne mettre que du code d'initialisation des variables d'instances dans un constructeur et de mettre les traitements dans des méthodes qui seront appelées après la création de l'objet.
Les conventions de nommage des entités permettent de rendre les programmes plus lisibles et plus faciles à comprendre. Ces conventions permettent notamment de déterminer rapidement quelle entité désigne un identifiant, une classe ou une méthode.
Entités | Règles | Exemple |
Les packages | Toujours écrits tout en minuscules (norme Java 1.2) | com.entreprise.projet |
Les classes, les interfaces et les constructeurs |
La première lettre est en majuscule. |
MaClasse MonInterface MaClasse() |
Les méthodes |
Leur nom devrait contenir un verbe. |
public float calculerMontant() { |
Les variables |
La première lettre est obligatoirement une minuscule et ne devrait pas être un caractère dollar '$' ou underscore '_' même si ceux-ci sont autorisés. Si le nom est composé de plusieurs mots, la première lettre de chaque mot doit être en majuscule, ne pas mettre de caractère underscore '_'. Les noms de variables composés d'un seul caractère doivent être évités sauf pour des variables provisoires (index d'une boucle). |
String nomPersonne; Date dateDeNaissance; int i; |
Les constantes | Toujours en majuscules, chaque mots est séparés par un underscore '_'. Ces variables doivent obligatoirement être initialisées lors de leur déclaration. |
static final int VAL_MIN = 0; static final int VAL_MAX = 9; |
L'usage des séparateurs tels que les retours à la ligne, les lignes blanches, les espaces, etc ... permet de rendre le code moins « dense » et donc plus lisibles.
L'unité d'indentation est constituée de 4 espaces. Il n'est pas recommandé d'utiliser les tabulations pour l'indentation.
Il est préférable d'éviter les lignes contenant plus de 80 caractères.
Elles permettent de définir des sections dans le code pour effectuer des séparations logiques.
Deux lignes blanches devraient toujours séparer deux sections d'un fichier source et les définitions des classes et des interfaces.
Une ligne blanche devrait toujours être utilisée dans les cas suivants :
Un espace vide devrait toujours être utilisé dans les cas suivants :
Exemple : |
while (i < 10) |
Exemple : |
a = (b + c) * d |
Exemple : |
for (int i; i < 10; i++) |
Exemple : |
i = ((int) (valeur + 10)); |
Il ne faut pas mettre d'espace entre un nom de méthode et sa parenthèse ouvrante.
Il ne faut pas non plus mettre de blanc avant les opérateurs unaires tels que les opérateurs d'incrément '++' et de décrément '--'.
Exemple : |
i++; |
Il arrive parfois qu'une ligne de code soit très longue (supérieure à 80 caractères).
Dans ce cas, il est recommandé de couper cette ligne en une ou plusieurs en respectant quelques règles :
Exemple : |
maMethode(parametre1, parametre2, parametre3,
parametre4, parametre5); |
Même s'il est possible de mettre plusieurs traitements sur une ligne, chaque ligne ne devrait contenir qu'un seul traitement :
Exemple : |
i = getSize();
i++; |
Elles correspondent à des instructions qui utilisent des blocs de code.
Les instructions incluses dans ce bloc sont encadrées par des accolades et doivent être indentées.
L'accolade ouvrante doit se situer à la fin de la ligne qui contient l'instruction composée.
L'accolade fermante doit être sur une ligne séparée au même niveau d'indentation que l'instruction composée.
Un bloc de code doit être définit pour chaque traitement même si le traitement ne contient qu'une seule instruction. Cela facilite l'ajout d'instructions et évite des erreurs de programmation.
Elle ne devrait pas utiliser de parenthèses sauf si celles-ci facilitent la compréhension.
Exemple : |
return;
return valeur;
return (isHomme() ? 'M' : 'F'); |
Elle devrait avoir une des formes suivantes :
Exemple : |
if (condition) {
traitements;
}
if (condition) {
traitements;
} else {
traitements;
}
if (condition) {
traitements;
} else if (condition) {
traitements;
} else {
traitements;
} |
Même si cette forme est syntaxiquement correcte, il est préférable de ne pas utiliser l'instruction if sans accolades :
Exemple : |
if (i == 10) i = 0; // cette forme ne doit pas être utilisée |
Elle devrait avoir la forme suivante :
Exemple : |
for ( initialisation; condition; mise à jour) {
traitements;
} |
Elle devrait avoir la forme suivante :
Exemple : |
while (condition) {
traitements;
} |
Elle devrait avoir la forme suivante :
Exemple : |
do {
traitements;
} while ( condition); |
Elle devrait avoir la forme suivante :
Exemple : |
switch (condition) {
case ABC:
traitements;
break;
case DEF:
traitements;
break;
case XYZ:
traitements;
break;
default:
traitements;
break;
} |
Il est préférable de terminer les traitements de chaque cas avec une instruction break et de l'enlever au besoin plutôt que d'oublier une instruction break nécessaire.
Toutes les instructions switch devrait avoir un cas 'default' en fin d'instruction : le traitement de tous les cas est une bonne pratique de programmation.
Même si elle est redondante, une instruction break devrait être incluse en fin des traitements du cas 'default' afin de généraliser la première recommandation.
Elle devrait avoir la forme suivante :
Exemple : |
try {
traitements;
} catch (Exception1 e1) {
traitements;
} catch (Exception2 e2) {
traitements;
} finally {
traitements;
} |
Il ne faut pas déclarer de variables d'instances ou de classes publiques sans raison valable.
Il est préférable de restreindre l'accès à la variable avec un modificateur d'accès protected ou private et de déclarer des méthodes respectant les conventions instaurées dans les javaBeans : getXxx() ou isXxx() pour obtenir la valeur et setXxx() pour mettre à jour la valeur.
La création de méthodes sur des variables private ou protected permet d'assurer une protection lors de l'accès à la variable (déclaration des méthodes d'accès synchronized) et éventuellement un contrôle lors de la mise à jour de la valeur.
Il n'est pas recommandé d'utiliser des variables ou des méthodes de classes à partir d'un objet instancié : il ne faut pas utiliser objet.methode() mais classe.methode().
Exemple à ne pas utiliser si afficher() est une méthode de classe : |
MaClasse maClasse = new MaClasse();
maClasse.afficher(); |
Exemple à utiliser si afficher() est une méthode de classe : |
MaClasse.afficher(); |
Il est préférable de ne pas utiliser des constantes numériques codées en dur dans le code mais de déclarer des constantes avec des noms explicites. Une exception concerne les valeurs -1, 0 et 1 dans les boucles for.
Il n'est pas recommandé d'assigner la même valeur à plusieurs variables sur la même ligne :
Exemple : |
i = j = k; //cette forme n'est pas recommandée |
Il ne faut pas utiliser l'opérateur d'assignement imbriqué.
Exemple à proscrire : |
valeur = (i = j + k ) + m; |
Exemple : |
i = j + k;
valeur = i + m; |
Il n'est pas recommandé d'utiliser l'opérateur d'assignation = dans une instruction if ou while afin d'éviter toute confusion.
Il est préférable d'utiliser les parenthèses lors de l'usage de plusieurs opérateurs pour éviter des problèmes liés à la priorité des opérateurs.
Exemple : |
if (i == j && m == n) // à éviter
if ( (i == j) && (m == n )) // à utiliser |
Il est préférable de minimiser le nombre d'instructions return dans un bloc de code.
Exemple à eviter : |
if (isValide()){
return true;
} else {
return false;
} |
Exemple : |
return isValide(); |
Exemple : |
if (isValide()) {
return x;
} else return y; |
Exemple à utiliser : |
return (isValide() ? x : y) |
Si la condition dans un opérateur ternaire ? : contient un opérateur binaire, cette condition doit être mise entre parenthèses.
Exemple : |
( i >= 0 ) ? i : -i; |
Java permet de déclarer les tableaux de deux façons :
Exemple : |
public int[] tableau = new int[10];
public int tableau[] = new int[10]; |
L'usage de la première forme est recommandé.
|