Utilisez des conventions de nommage
Appliquez des règles de nommage simples
Les conventions de nommage sont un ensemble de règles qui permettent de rendre le code plus lisible pour un humain. La raison est simple, tout notre code est rempli de noms d'éléments : variables, fichiers, répertoires, fonctions... Et chacun de ces éléments représente quelque chose en particulier. Appelons-le de la manière la plus claire possible pour savoir ce qu'il fait.
Comme vous allez le voir dans cette section, la clarté des noms que l’on apporte à nos éléments de code permet à l’équipe de gagner du temps et collaborer plus efficacement. C'est donc un critère de qualité professionnelle. Le plus important est donc de choisir une manière d’écrire son code, une convention de nommage, et de s’y tenir tout du long.
Je vous propose donc de voir certaines règles qui facilitent ce travail.
Gardez à l’esprit que le nommage de nos classes, fonctions et autres variables devrait permettre à un développeur extérieur de comprendre ce que cet élément fait, et ce, rien qu’en lisant son nom.
Les noms devraient donc être lisibles par un humain et compréhensibles.
Gardez en tête que les noms que vous choisissez devraient exprimer une intention.
Pour illustrer ce point, prenons le code suivant :
public double [] solPol2(double a, double b, double c){
double tab[] = {};
double d = b * b - 4.0 * a * c;
if (d < 0.0) {
System.out.println("Pas de solutions reelles");
} else if (d > 0.0) {
return tab[(-b - Math.sqrt(d)) / (2.0 * a), (-b + Math.sqrt(d)) / (2.0 * a))] ;
} else {
Return tab[-b / (2.0 * a)];
}
}
Ce code est assez difficile à lire :
À quoi sert
tab
?Que sont
a
,b
etc
?À quoi sert
d
?
Apportons un peu de clarté dans le code :
public double [] solutionPolynomeDegre2(double coeffA, double coeffB, double coeffC){
double solutions[] = {};
double discriminantPolynome = coeffB * coeffB - 4.0 * coeffA * coeffC;
if (discriminantPolynome < 0.0) {
System.out.println("Pas de solutions reelles");
} else if (discriminantPolynome > 0.0) {
return solutions[(-coeffB - Math.sqrt(discriminantPolynome)) / (2.0 * coeffA), (-coeffB + Math.sqrt(discriminantPolynome)) / (2.0 * coeffA))] ;
} else {
Return solutions[-coeffB / (2.0 * coeffA)];
}
}
Qu’est ce que j’ai fait ici ?
solPol2
est en fait une fonction qui permet de calculer la solution à un polynome de degré 2, je l’appelle doncsolutionPolynomeDegre2
;a
,b
etc
sont les coefficients du polynôme, je les appelle donc de manière plus claire :coeffA
,coeffB
etcoeffC
;tab
est en fait le tableau contenant les solutions de l’équation, je l’appelle doncsolutions
.
Ainsi, la fonction redevient lisible et compréhensible sans avoir ajouté trop de code.
Choisissez une convention existante, la notation hongroise
Tout d’abord, sachez qu’il existe une multitude de conventions qui permettent d'homogénéiser le code.
Par exemple, la convention de nommage dite “Camel case” prône que chaque mot commence par une majuscule sauf le premier qui commence par une minuscule, comme dans l’exemple ci-dessous. Elle est assez répandue dans les communautés de développeurs JavaScript ou C#.
const passwordHashed ;
Un autre exemple répandu est la convention dite “Pascal case”, où les mots sont liés sans espace et chaque nom commence par une majuscule. Cette convention est particulièrement visible dans les communautés PHP et Java.
String PasswordHashed ;
Enfin, vous pourrez également rencontrer la convention dite “Snake case”, dans laquelle chaque mot est séparé par un underscore “_”. Elle est utilisée par certains développeurs Ruby ou bien encore Python.
password_hashed = "$2$aefbofnbqofiudjbqodsihazfeo"
De manière générale, en plus des conseils précédents, je vous recommande d'utiliser des verbes pour vos méthodes de classes.
Tout cela peut vous paraître difficile. Pour commencer à implémenter des conventions de nommage adéquates, je vous recommande d'utiliser (au moins au début) la notation hongroise. Elle est basée sur du Camel case à laquelle quelques règles ont été ajoutées ; et c’est un moyen simple pour construire des noms lisibles et cohérents.
D’après cette notation,
le nom est typé puis suivi par un qualificateur. Ces 2 identifiants doivent être séparés soit par une barre de soulignement ou bien une différence entre majuscules et minuscules ;
le qualificateur assure la cohérence des noms de données de même type ;
on constitue les types simples avec des mots très courts pour permettre la construction de types complexes.
Le tableau suivant vous donne quelques exemples.
Qualificateur | Type | Exemple |
i | int | int iNbVoitures |
str | string | string strNomProprietaire |
b | bool | bool bPileOuFace |
Ce type de notation, bien qu’assez ancien et sujet à débat, peut servir de base à des conventions de nommage pour vos variables et fonctions. N'hésitez pas à l'adapter à votre projet et vos besoins !
Après ces remarques plutôt générales, dans la prochaine section, j’aimerais faire un focus plus particulier sur la lisibilité de nos classes et fonctions.
Apportez de la lisibilité aux éléments importants de votre code
Avant de voir plus particulièrement les classes et fonctions, je vous conseille d’adopter une règle générale : avant chaque élément majeur (fichier, classe, fonction, etc.), un cartouche descriptif en commentaire est recommandé. Il s’agit d’un bloc de commentaire qui peut être formaté d’une certaine manière, et qui explique le fonctionnement du code qui suit.
Il existe de nombreux outils capables de générer une documentation de manière automatique si ces commentaires sont correctement formulés.
L’exemple de Javadoc
À titre d’illustration, Javadoc permet en inspectant le code Java de produire la documentation de votre code en utilisant la ligne de commande, ou bien avec l’IDE Eclipse, par exemple.
Cet outil s’appuie sur des conventions de nommage (vous faites le lien maintenant ? :)) pour produire une documentation au format HTML.
Après cette courte introduction, passons à nos classes et fonctions.
Nommez et documentez vos classes
Les classes sont importantes car elles constituent la logique de haut niveau de nos applications. S’intéresser à comment écrire des classes propres est donc un enjeu majeur pour vous.
Tout d’abord, je vous recommande de respecter une organisation simple et cohérente :
Les constantes à utiliser écrites en majuscule, s’il y en a.
Une liste de variables.
Ensuite les fonctions publiques.
Et enfin les fonctions privées si nécessaire.
Ensuite, n’hésitez pas à ajouter un cartouche explicatif comme nous l’avons vu dans la section précédente.
Ceci a plusieurs avantages :
la lisibilité de votre code est améliorée ;
l’automatisation de la documentation sera plus facile.
Dans les 2 exemples suivants, je vous partage deux classes qui suivent ces quelques règles, afin d’en faciliter la documentation.
/**
* Represents an app user
*
* @author Francis
* @version 1.1
*/
Package app;
import app.Password.Class;
public class User {
private final String name;
private final Password password;
private final Email emailAddress;
public User(final String name, final Password password, final Email emailAdress) {
this.name = name;
this.password = password;
this.emailAdress = emailAddress;
}
public String getName() {
return name;
}
public String getPassword() {
return password;
}
public Email getEmailAddress() {
return emailAdress;
}
public void setName(final String name) {
this.name = name;
}
public String setPassword(final String password) {
Password.hashPassword(password);
}
public void setemailAdress(final String emailAddress) {
this.emailAddress = emailAddress;
}
}
Dans cet exemple, vous pouvez voir que j'utilise un cartouche de commentaire avant la classe User, dans lequel je décris ce que représente la classe (un utilisateur de l'application), ainsi que l'auteur et la version de la classe.
Aucun commentaire n'est nécessaire dans la classe car tous les noms sont explicites et clairs.
Package app;
/**
* Defines methods to encrypt passwords
* Use the Bcrypt tools
*
* @version 1.1
*/
public class Password {
/**
* Options for the Bcrypt algorithm
*/
private static final int WORKLOAD = 12;
/**
* Used to generate a string representing an account password.
*
* @param password_plaintext The account's plaintext password as provided during account creation,
*
* @return String - the bcrypt hash password
*/
public static String hashPassword(final String passwordPlaintext) {
final String salt = BCrypt.gensalt(WORKLOAD2);
final String hashedPassword = BCrypt.hashpw(passwordPlaintext, salt);
return (hashedPassword);
}
/**
* Used to verify a computed hash from a plaintext with that of a stored hash from a database.
*
* @param password_plaintext The account's plaintext password, as provided during a login request
* @param stored_hash The account's stored password hash, retrieved from the authorization database
* @return boolean - true if the password matches the password of the stored hash, false otherwise
*/
private boolean checkPassword(final String passwordPlaintext, final String storedHash) {
boolean passwordVerified = false;
if(null == storedHash || !storedHash.startsWith("$2a$"))
throw new java.lang.IllegalArgumentException("Invalid hash provided for comparison");
passwordVerified = BCrypt.checkpw(passwordPlaintext, storedHash);
return(passwordVerified);
}
}
Dans cet exemple, beaucoup plus de commentaires car les fonctions sont un peu plus compliquées. J'ai donc mis un cartouche pour expliquer le rôle de la constante WORKLOAD.
De plus, j'ai créé un cartouche pour la méthode hashPassword
et un pour la méthode checkPassword
. Vous pouvez remarquer 2 éléments dans ces cartouches, en plus du texte descriptif :
@param
suivi d'un nom de paramètre de la méthode : c'est un mot clé de Javadoc qui permet de spécifier que l'on décrit un paramètre ;@return
suivi d'un type de variable : c'est un autre mot clé de Javadoc qui permet de spécifier que l'on décrit la valeur de retour de la méthode et son type.
Nommez et documentez vos fonctions
Pour améliorer la lisibilité de vos fonctions, vous pouvez vous inspirer des quelques règles suivantes.
Tout d’abord, nous avons déjà parlé de l’importance du nommage. Cette règle s’applique naturellement aux fonctions. Choisissez des noms de fonctions qui décrivent ce qu’elles font réellement, même si cela implique un nom assez long.
Je vous conseille également de garder le même champ lexical dans vos noms de fonctions comme addProduct
, deleteProduct
, updateProduct
, createProduct
, addProductToShoppingCart
.
Cette manière de faire va considérablement améliorer la lisibilité de votre code mais également faciliter votre réflexion, comme si vous racontiez une histoire.
Ensuite, concernant les arguments, je vous conseille de les réduire au maximum, car ils compliquent la lecture et rendent les tests difficiles.
Comme nous l’avons déjà vu, n’hésitez pas à placer un cartouche sur les fonctions qui requièrent une explication plus poussée.
Enfin, je vous invite à vous débarrasser au plus vite des fonctions qui ne sont appelées nulle part, afin de ne pas nuire à la cohérence de votre code et à sa compréhension.
Formatez votre code !
Naturellement, le code que vous produisez doit être fonctionnel, c’est le minimum attendu d’un développeur. Mais un autre aspect est primordial, c’est la communication. Elle est souvent relayée au second plan par les développeurs pour diverses raisons, mais elle doit faire l’objet de votre souci permanent. Un code bien écrit est un code qui se lit facilement.
Convaincu ? Alors, voyons quelques règles simples pour rendre votre code fonctionnel et professionnel.
Le formatage vertical
La manière dont vous allez organiser verticalement votre code va considérablement impacter sa lisibilité.
Je vous recommande d’espacer d’une ligne les blocs de code qui possèdent la même portée, pour les séparer du reste du code. Ceci aura pour conséquence de densifier les blocs de code dont la signification fonctionnelle est proche.
package app.ecole ;
public class Etudiant {
private Nom nom;
private int identifiant;
public Etudiant(String prenom, String nomFamille, int n) {
nom = new Nom(prenom,nomFamille);
numero = n;
}
public void changerNomFamille (String s) {
nom.changerNomFamille(s);
}
public void changerPrenom (String s) {
nom.changerPrenom(s);
}
public void changerNumero (int n) {
numero = n;
}
public String toString() {
return (nom.toString() + "\nNumero : " + numero);
}
}
Dans cette classe,
les variables sont déclarées ensemble dans un même bloc ;
les méthodes sont séparées chacune d’une ligne pour assurer la compréhension et la lisibilité.
Voici le même code sans règles. Qu’en pensez-vous ? C’est beaucoup moins clair, non ?
package app.ecole ;
public class Etudiant {
private Nom nom;
private int identifiant;
public Etudiant(String prenom, String nomFamille, int n)
{
nom = new Nom(prenom,nomFamille);
numero = n;
}
public void changerNomFamille (String s) {
nom.changerNomFamille(s);
}
public void changerPrenom (String s) {
nom.changerPrenom(s);
}
public void changerNumero (int n) {
numero = n;
}
public String toString() {
return (nom.toString() +
"\nNumero : " + numero);
}
}
Formatage horizontal
Pour améliorer la lisibilité horizontale de votre code, je vous recommande de garder les lignes de code les plus courtes possibles et d’utiliser les espaces pour donner de la clarté aux opérateurs.
L’exemple classique suivant d’un tri par sélection permet d’illustrer ce point.
public class Tri{
public static void trier(int[] tableau){
int i, j, cle;
for (i = 1; i < tableau.length; i++){
cle = tableau[i];
j = i;
while ((j >= 1) && (tableau[j - 1] > cle)){
tableau[j] = tableau[j - 1] ;
j = j - 1;
}
tableau[j] = cle;
}
}
}
Un autre point fondamental est la fameuse indentation. Elle a pour objet de bien marquer la hiérarchie des portées des éléments qui sont déclarés dans un périmètre cohérent. Respectez-la ! Sans une indentation propre, votre code ne pourra pas être lu par un humain.
Certains outils peuvent vous aider, comme Beautify, une extension pour Visual Studio, qui permet d’automatiser l’indentation de votre code JS, HMTL, CSS et JSON. Cerise le gâteau, vous pouvez même configurer l'indentation !
En résumé
Dans ce chapitre, nous avons vu en détail comment écrire du code autodocumenté, c’est-à-dire qui est écrit et organisé d’une manière telle qu’il est facilement compréhensible par un humain. Nous avons vu que :
Les conventions de nommage permettent de donner de la lisibilité et de la cohérence aux noms des données que nous utilisons.
Un effort particulier doit être réalisé sur les objets et les fonctions. Ce sont des éléments importants qui méritent de l’attention dans la manière de les écrire.
Pour rendre un code agréable et professionnel, quelques règles de formatage et de factorisation sont à mettre en place.
Dans le prochain chapitre, je vous invite à voir comment faciliter l’écriture de la documentation de votre code.