Sujet quasi-religieux, s'il en est un... L'indentation est le principe selon lequel la disposition du code doit refléter sa structure. Les programmes étant des entités complexes comprenant souvent des millions de lignes de texte, l'indentation est une nécessité pour qui souhaite être en mesure de lire, comprendre et – bien sûr – assurer l'entretien des programmes.
J'utilise souvent des langages à accolades (C, C++, Java, C#) pour exprimer de manière programmatique mes idées. Pour cette raison, je joins disposition des accolades et disposition générale du texte dans cette rubrique.
Voici donc ma pratique personnelle d'indentation quand je programme pour mes propres fins. Quand je programme dans une équipe ou quand je produis quelque chose qui ne soit pas destiné à moi ou à mes étudiant(e)s, je me conforme aux usages du groupe, qui sont plus importants que mon confort personnel. De même, quand j'utilise un langage comme Go ou JavaScript ou la position des accolades (ouvrante, typiquement) influence la compréhension qu'a le compilateur de notre code source, je respecte bien sûr les usages.
Je faisais... | ...je fais maintenant | |||||
---|---|---|---|---|---|---|
Règle générale, pour les instructions sur plus d'une ligne, j'utilise des accolades alignées l'une vis à vis l'autre et situées à la hauteur de la séquence en cours (le style Allman selon ce Wiki), mais je n'en fais pas une religion... sauf pour mes étudiant(e)s lorsqu'elles ou ils débutent; dans ce cas, il faut être rigoureux, à la limite sévère, pour réduire dans leur vie les bogues qui font inutilement perdre du temps. J'indente avec des espaces. J'utilise un nombre impair d'espaces, typiquement trois (à cinq, je trouve que c'est excessif). Cela fait un espace pour l'accolade et deux pour la disposition. J'utilise toujours une police non proportionnelle (un truc que j'ai appris tôt dans ma carrière), justement pour améliorer l'impact de l'indentation. Pour des instructions sur une seule ligne, je tends à indenter tout simplement et à omettre les accolades, sauf dans les cas où je me doute très fort que je devrai en ajouter de toute manière éventuellement. J'ai longtemps pesté contre les fonctions à plusieurs points de sortie, mais avec le temps, j'ai fini par les intégrer à ma pratique, essentiellement pour la gestion des cas d'erreurs ou d'exceptions. Dans ces cas, il m'arrive même (il y a des exemples à droite) d'insérer le test et le traitement d'erreur ou d'exception sur une même ligne. Ce n'est pas un style convenable pour des débutants, cependant (trop facile de faire des erreurs). Je définis mes constantes et mes variables aussi localement que possible (le code à droite est un exemple insuffisamment modularisé; un type Age connaissant ses bornes serait avantageux. En pratique, je manipule rarement des primitifs directement comme cet exemple le fait). J'ai changé mes pratiques d'indentation récemment en 2016 pour ce qui est du positionnement des accolades. La raison pour ce changement est simple : l'adaptation aux outils pédagogiques. De plus en plus, les écoles où j'enseigne et les lieux où je suis appelé à donner des présentations offrent des écrans plutôt que des ardoises – il faut savoir que je suis un « gars de craie », pas un « gars de diapositives électroniques »; j'aime bien le dynamisme que permettent les ardoises, où il est possible de changer d'approche avec agilité. Sur les écrans, pour faciliter la vie des étudiantes et des étudiants au fond de la salle, j'utilise des polices de caractères plus grandes que ce que j'utiliserais en temps normal. Une conséquence de ce changement est que si je place les accolades ouvrantes sous le mot qui introduit un bloc, cela tend à occuper un espace vertical important sans livrer d'information visuelle utile. Comparez vous-mêmes :
Ceci me permet de maximiser l'utilisation de l'écran et l'utilité de l'information présentée. Notez que c'est aussi un retour à mes pratiques du milieu des années '90 alors que je travaillais à CAE Électronique Ltée. |
|
|
J'ai mis de côté ma politique de « un seul return par fonction » au fil des années (voir XXX pour plus d'informations à ce sujet). Sachant que C++ 17 permet de déclarer des variables dans une alternative (if) ou dans une sélective (switch) pour limiter leur portée, en pratique, plutôt que d'écrire ceci :
// ...
short lire_age() {
const short AGE_MIN = 0,
AGE_MAX = 140; // estimé
using std::cin;
short age;
if (!(cin >> age)) throw erreur_lecture{};
if(!est_entre_inclusif(age, AGE_MIN, AGE_MAX))
throw hors_bornes{};
return age;
}
// ...
... j'écrirais aujourd'hui cela :
// ...
short lire_age() {
const short AGE_MIN = 0,
AGE_MAX = 140; // estimé
using std::cin;
if (short age; cin >> age) {
if(!est_entre_inclusif(age, AGE_MIN, AGE_MAX))
throw hors_bornes{};
return age;
}
throw erreur_lecture{};
}
// ...
... ou encore simplement cela :
// ...
short lire_age() {
const short AGE_MIN = 0,
AGE_MAX = 140; // estimé
using std::cin;
if (short age; cin >> age)
rerurn est_entre_inclusif(age, AGE_MIN, AGE_MAX)? age : throw hors_bornes{};
throw erreur_lecture{};
}
// ...
... qui est plus hygénique (la portée des variables est mieux circonscrite) et plus direct.
Je faisais... | ...je fais maintenant | |
---|---|---|
Dans le cas de petites fonctions sur une seule ligne, il m'arrive d'utiliser le format proposé à droite (indentation, accolade, code, accolade), surtout sur la ligne est courte. Ceci se produit surtout dans la définition inline (à même la déclaration) de méthodes. Le fait que j'aie fréquemment recours à de la programmation générique a pour conséquence (entre autres choses) que j'écris beaucoup, beaucoup de très petites fonctions faciles à optimiser comme celle-ci. |
|
|
Pour une classe, ça donne quelque chose comme ce que vous pouvez voir à droite. Vous remarquerez que la plupart de mes services sont sur une seule ligne (j'écris rarement des fonctions de plus d'une ligne, en pratique; à droite, le seul cas de plus d'une ligne est l'opérateur de consommation d'un flux, parce qu'il implique de la validation). Cela fait une forme relativement compacte et qui, à mon avis, se lit quant même bien. |
|
|
Note pragmatique
En pratique, déboguer prend du temps et, même avec les meilleures pratiques de programmation, il est inévitable que l'on se trouve, au moins sur une base occasionnelle, à fourbir les armes et à recourir au débogueur.
Certains débogueurs gèrent moins bien les fonctions où les accolades apparaissent sur une seule ligne, accompagnées de la définition de la fonction. Dans de tels cas, pour me faciliter l'existence, il m'arrive de faire des retouches temporaires pour remplacer une fonction indentée comme suit :
double carre(double x)
{ return x * x; }
... par son équivalent indenté comme cela :
double carre(double x)
{
return x * x;
}
... ou encore, plus souvent aujourd'hui pour les raisons mentionnées plus haut :
double carre(double x) {
return x * x;
}
Devoir reformater le code est un irritant, alors ces adaptations sont pour moi des situations temporaires. Cela dit, si vous déboguez beaucoup, il se peut que vos choix d'indentation soient influencés par les forces et les faiblesses de vos outils.
Quelques liens pour enrichir le propos.