Remarques sur les habitudes de programmation
But
Le but est de pouvoir travailler en équipe. Un individu peut très bien être capable d'écrire et maintenir une fonction de 500 lignes. Là n'est pas le problème.
Le problème est que quand un collègue devra travailler avec cette fonction, l'utiliser ou la modifier, il ne pourra pas être 100% sûr de son utilisation, ou de l'emplacement où insérer du nouveau code. On peut être quasiment sûr que cela introduira de nouveaux bugs, et le responsable sera l'auteur original de cette fonction non maintenable. De plus cela consommera du temps de développement supplémentaire inutilement.
Généralités
- Certains considère que du code moderne doit utiliser STL et Boost ; d'autres que ces librairies sont à éviter autant que possible. Là n'est pas le propos de cette page, cela dépend trop de votre équipe, de la plateforme, et c'est très subjectif. Par contre je conseille fortement l'utilisation de la STL dans tout ce qui est outils.
- À éviter: abstraire trop tôt, avant de vraiment savoir si, comment et pourquoi quelque chose doit être généralisé.
- Ne pas abuser du polymorphisme et des héritages. Une classe intégrant des composants sera souvent plus propres qu'une classe héritant de comportements. Exemple: "la classe BreakableSlidingDoor dérivera t'elle de BreakableDoor ou de SlidingDoor?" vs une classe Entity ayant des pointeurs sur différents composant, à NULL si le comportement n'est pas présent (déplacement, gestion vie et stats, AI, physique, rendu...).
- "Premature optimization is the root of all evil (optimiser trop tôt est la racine de tous les maux)" (par Sir Tony Hoare) est complètement faux, comme le montre la citation complète de Sir Tony Hoare (autre source):
We should forget about small efficiencies, say about 97 of the time: premature optimization is the root of all evil.
Optimiser trop tôt au niveau local, dans une fonction particulière (ex: réécrire en asm) est généralement mal. Optimiser en architecturant correctement et en utilisant les bons algorithmes est une nécessité pour être un bon programmeur. Essayer de faire cela en fin de vie d'un projet sera plus difficile voire impossible, moins efficace, plus couteux et ralentira le développement du projet. - Extreme-programming: mauvais nom pour regrouper des idées qui existaient déjà auparavant. Pourquoi créer un nouveau buzzword et pourquoi utiliser un terme si péjoratif ("extreme")?
- Peer-programming: très bon pour les specs.
Code lisible
- Pas de fonctions trop longues (un écran max, soit ~40 lignes).
- Pas de variables au nom trop court et trop générique: dirPlayerToTarget à la place de dir.
- Pas de copier-coller de gros blocs de code. Factorisez le code commain en fonction.
- Utiliser size_t pour caster un pointeur en integer (par exemple pour des comparisons ou des calculs d'offsets).
- Ne pas réutiliser une variable locale, en créer une nouvelle avec un nom spécifique à chaque fois. Cela permet en plus au compilateur de voir facilement que la valeur précédente n'est plus utilisée ni à conserver ; donc d'optimiser plus.
- Lire une seule fois les variables membres, et les stoquer dans des variables locales constantes au besoin : cela permet au compilateur de savoir qu'il n'y a aucun aliasing mémoire et qu'il peut optimiser à fond.
- Commentaire sur chaque #else et #endif indiquant le test #if correspondant.
- Pas de sortie de fonction en plein milieu, doit être soit en tout début pour vérification des paramètres, soit en fin de fonction.
- Pas d'accès répétés à des globales ou singletons : utiliser des références ou copie locale. Gain en lisibilité et en optimisation (moins d'aliasing mémoire, moins d'appels que souvent le compilateur ne peut optimiser...)
De même souvent avec les variables membres : si une boucle met à jour souvent une variable membre, il est souvent préférable de la copier dans une variable locale, de modifier cette dernière dans la boucle, puis de la copier dans la variable membre. - Pas de nombres magiques : pas de constantes en dur dans le code. Un "*2" qui traine peut parfois être acceptable. Mais je vais donner un exemple concret de pourquoi cela est mal : un collègue avait écrit un système de chargement asynchrone de niveau dans un jeu ; le système reposait sur des fonctionnalités hardware ne pouvant charger des données que par blocs de 512 octets et seulement à des adresses mémoires multiples de 512 octets. Exemple de lignes de code avant nettoyage :
- sizeBloc = (size>>9)<<9;
- assert( (destAddress & 511) == 0 );
Exemple après nettoyage:- sizeBloc = size & ~(HARDWARE_BLOCK_SIZE-1); // Et oui l'API de la console avait une constante définie. De plus ce "&" est plus lisible et plus rapide à exécuter.
- assert( (destAddress & (HARDWARE_BLOCK_SIZE-1) == 0 );
Code plus lisible, un poil plus rapide, et fonctionnant toujours si une MAJ de l'architecture change la taille des blocs streamables.Pas de préfixe "C" sur le nom des classes. Cette habitude a été prise par les programmeurs de Microsoft pour différencier leurs classes de celles des autres programmeurs, mais de nombreux autres programmeurs copiant cette habitude, elle est devenue non seulement inutile et contre-productive.Pointeurs vs References : utiliser une référence a l'aventage d'indiquer qu'une valeur NULL est incorrecte (ce qui n'interdit pas de la tester, soit par un if, soit par un assert) ; utiliser un pointer a l'avantage de pouvoir le tagger comme restricted, permettant au compilateur de mieux optimiser.Standardiser le code au sein de votre équipe. Voici quelques exemples de règles, mais vous pouvez avoir d'autres habitudes (chaque entreprise, voire chaque projet, a ses propres standards):- J'aime bien les notations dite Pascal/Java pour les noms de classes, méthodes et fonction, et Camel pour les variables : pas de '_' dans les noms, majuscule à chaque mot du nom, sauf pour le premier des variables: float speed, int GetSpeed().
- Différencier les variables membres des variables locales. Soit par préfixe (m ou m_ ou autre), ou par suffixe. Éviter le préfixe ou le suffixe '_' car le standard C/C++ l'interdit. Petit avantage des suffixes: plus rapide à (ne pas) taper avec l'auto-complétion ; mais moins lisibles pour certains.
- Hungarian Notation: j'ai une préférence pour une notation très simplifié n'indiquant que les statiques, globales, tableau C et string C. Pas d'indicateur de pointeurs/entier/floats... Mais chaque équipe a ses propres préférences, à chacun de s'adapter.