nombre = (sansFil || nombreDeTouches >= 108) ? 20 : 30;
nombre = (nombre == 20 && (sansFil && nombreDeTouches >= 108)) ? 40 : 50;
- modularité: Autant que faire se peut et suivant le langage ou le paradigme choisi, il convient d'utiliser au mieux les capacités modulaires du langage. Les fonctions utiles à un même traitement général seront regroupées dans un même fichier source. Un source sera donc divisé en plusieurs fichiers correspondants à des modules ou des parties importantes et faisant sens par elle-même. Dans le cas d'une voiture, les modules correspondraient aux sous-sytèmes globaux indépendants: système électrique, moteur, direction, carrosserie, accessoires...
- organisation générale : Respecter les normes d'organisation du langage utilisé permet une relecture plus aisée puisque le lecteur saura où trouver dans le code les déclarations et les informations qu'il souhaite.
exemple pour le C avec un module mon_module.c :
1. tous #include dans l'ordre <xxx.h>, "yyy.h", "mon_module.h"
2. tous les #define.
3. description des modèles de structures (struct)
4. déclaration de toutes les fonctions restreintes au fichier (static)
5. déclaration de des variables globales restreintes au fichier (static)
6. définition des fonctions exportées
7. définition des fonctions static
le fichier d'en-tête mon_module.h est organisé comme suit:
1. #ifndef MON_MODULE_H
#define MON_MODULE_H
2. tous les #include <xxx.h>, "yyy.h"
3. tous les #define des symboles exportés
4. les typedef des modèles de structure exportées
5. description complète des modèles de structure exportés
6. prototypes des fonctions exportés
7. #endif
- décomposition fonctionnelle: Chaque fonction ne doit réaliser qu'une seule tâche ou n'implémenter qu'un unique algorithme, en réponse au problème à résoudre. Cette tâche ne doit cependant pas être trop unitaire (rien ne sert de créer une fonction pour soustraire deux nombres), ni trop globale (une fonction de tri par tas utilisera elle-même la fonction tamiser). Il appartient à l'auteur de trouver l'équilibre permettant une lecture facile du code, sans multiplier toutefois les renvois.
- variables intermédiaires : A l'intérieur d'une fonction ou d'un bloc effectuant des calculs longs, il convient d'utiliser des variables intermédaires, sans pour autant les multipliers artificiellement. En règle générale, on convient que l'usage d'une variable intermédiaire est valable si celle-ci a une signification propre au regard du problème considéré. Dans un langage à la structure stricte comme le C, les variables temporaires sont déclarées en début de bloc. Lorsque c'est possible en revanche, comme en C++, il sera préféré une déclaration à l'endroit d'utilisation. Notons également qu'il est inutile de créer une variable intermédaire pour récupérer une valeur à retourner. Il est possible de retourner directement l'expression:
return (expression);
exemple correct:
//------
//150 lignes de programmes
while (nb_voiture < 3) {
int ma_variable;
/*
* 150 lignes n'utilisant pas ma_variable
*/
ma_variable = ...;
}
// 150 lignes n'utilisant plus ma_variable
//------
exemple encore plus correct:
//------
// 150 lignes de programmes
while (nb_voiture < 3) {
/*
* 150 lignes n'utilisant pas ma_variable
*/
int ma_variable;
ma_variable = ...;
}
// 150 lignes n'utilisant plus ma_variable
//------
exemple incorrect:
//------
// 150 lignes de programmes
int ma_variable;
while (nb_voiture < 3) {
/*
* 150 lignes n'utilisant pas ma_variable
*/
ma_variable = ...;
}
// 150 lignes n'utilisant plus ma_variable
//------
- structure de contrôle conditionnel / inconditionnel: Il convient de veiller à utiliser les contrôles de condition les mieux adaptés à une situation. En règle générale, l'emploi de l'instruction switch + case + break + default est préférable à une longue liste de if + else.
- structure de boucle: Lorsque le nombre d'itération d'une boucle est connu ou peut être déterminé comme intervalle entre deux expressions, il est préférable d'utiliser une boucle for. Dans le cas où au contraire le nombre d'itération dépend d'une condition à remplir, la boucle while sera privilégiée.
- omission de renvoi d'une fonction: Dans certains cas, lorsqu'une fonction renvoit un paramètre non utilisé par la suite dans le programme, il est inutile de récupérer cette valeur de retour. En revanche, pour les fonctions critiques renvoyant un indice de réussite (0= succès; -1= échec; 1=erreur de dépassement....), l'étude attentive de ce paramètre de retour permet une résolution et un traitement d'erreur efficace.
exemple incorrect:
int code_retour;
code_retour = executer_ma_fonction();
//code_retour jamais utilisé
exemple correct:
// avant...
executer ma_fonction();
// après, code_retour jamais utilisé..
Conventions de restrictions
- étiquettes et labels: Dans la plupart des langages, il convient d'exclure les possibilités d'étiquettes et de labels. Ainsi l'instruction
goto peut dans 99.99% des cas être évitée par l'utilisation appropriée de
break,
continue ou
return ainsi que de conditions sur une variable booléenne. Le problème dans l'usage des goto est la génération d'un code difficile à relire par la multiplicité des renvois, parfois à des endroits complètement différents où incongrus. L'ordre de l'exécution des fonctions n'est plus l'ordre de lecture et le code résultant devient artificiellement compliqué à relire. On appelle un tel code un
code spaghetti -et cela constitue presque une insulte.
- variables globales globales et variables globales restreintes: Une variable globale est accessible et modifiable partout dans le code, même dans les fichiers séparés. Cette pratique n'est pas conseillée voir prohibée sauf si la variable globale est définie comme constante. Une variable globale peut cependant être restreinte au fichier dans lequel elle est déclarée (en C par l'emploi de static).
Deux conseils inclassables
- Couper n'est pas copier: D'une manière générale, la fonction copier pourrait être désactivée des environnements de développement. Dite ainsi, la mesure paraît drastique, mais l'explication est simple. Si vous avez besoin de copier-coller un bout de code d'un endroit à un autre de votre programme, c'est très souvent que ce morceau mérite d'être transformé en une fonction (ou en méthode en Java). Ainsi, vous n'aurez plus besoin de rallonger votre code mais pourrez faire un appel à la-dite fonction. Pondérez cependant ce fait en considérant qu'une multiplication excessive de fonctions est nuisible à la lecture et la compréhension rapide du code. Comme pour chacune des préconisations de cet article, tout est une question de compromis.
- Supprimer le code mort: Bien souvent dans les contributions, il est donné de voir des parties entières de code mises en commentaires. Ces parties de code étant obsolètes, l'auteur les a commentées sans les supprimer: dommage! Bien plus grave, quand des parties de code mort (jamais exécutées) sont laissées tel quel, même pas commentées. Toute fonction, méthode, classe, structure ou variable n'étant plus utilisé doit être supprimée du code.