Questions de nomenclature

Si vous cherchez les textes suivants, ils ont été relocalisés sur des pages à part entière :

« The lazy habit of shoving 'get' on the front of method names can encourage ambiguous names, e.g., getEntryNumber instead of numberOfEntries » – Kevlin Henney (source)

« On the (mis)naming habit of pasting ever more prefixes and suffixes to create identifiers: This is like homeopathy. What you've done is you've diluted the meaning until it's all gone » – Kevlin Henney (source)

Bien nommer les entités qui apparaissent dans nos programmes (variables, constantes, classes, fonctions, etc.) est plus difficile qu'il n'y paraît à première vue. Pourtant, le choix d'un nom est un geste crucial à bien des égards.

Une réflexion personnelle sur la question suivra, quand j'aurai quelques minutes, mais en attendant, voici quelques réflexions d'autres individus. Ceci vous aidera peut-être à vous forger une vision personnelle :

Bon ou mauvais nommage

« Pro Tip: don't try and name a function by looking at its implementation as you'll only come up with a name that reflects how it works. Instead look at the call sites and see what they want to know or do » – Chris Oldwood (source)

Quelques réflexions sur ce qui fait la qualité d'un bon nom :

Difficultés inhérentes au nommage

À propos de la difficulté inhérente du nommage :

Concision ou longueur des noms

« Rule of thumb: Variable name lengths should be proportional to their scope » Source : https://twitter.com/CompSciFact/status/971425146178342914

À propos de la concision :

Pratiques de nommage

À propos de diverses pratiques quant au nommage. Notez que Standards-programmation.html liste plusieurs guides connus en ce sens :

CamelCase, reverseCamelCase, snake_case, etc.

« CamelCase is popular because it’s part of OOP culture, which was shaped by Smalltalk, which used CamelCase because it does not allow _ in identifiers, because it used an early version of ASCII which had a left-pointing arrow instead of _, and it used the arrow for assignment » – Tony Finch (source)

Il y a des écoles de pensées sur le choix des majuscules, des minuscules, des soulignements, etc. dans les noms d'identifiants. Évidemment, la plupart des approches se valent, dans la mesure où elles sont appliquées de manière cohérente (d'où le volet stylistique des standards de programmation appliqués par diverses entreprises).

Constantes littérales ou symboliques

À propos des constantes littérales ou symboliques :

Ce que je fais, personnellement

Tout d'abord, la priorité pour quiconque souhaite un emploi (et le garder!) est de respecter les standards en place (par exemple, celles de Java, celles de .NET, celles de Google, etc.). Parfois, ceux-ci s'imposent d'office, et quelques règles simples peuvent servir de base à la réflexion.

Au fil des ans, mes usages ont changé quant au recours aux majuscules, aux minuscules et aux soulignements (ce qui transparaît sans doute dans les exemples sur ce site, qui ont été écrits sur une période de plusieurs années). Je tends maintenant à me limiter aux minuscules et aux soulignements (pour séparer les mots). Le recours à des mots français ne me pose pas de problème, mais si vous oeuvrez pour une entreprise qui privilégie des noms anglais ou autre chose, évidemment, respectez les normes en place.

En pratique, pour mon propre code, ce que je fais ressemble à ce qui suit.

Fonctions, méthodes, procédures

J'essaie d'utiliser des verbes pour mes noms de fonctions et pour mes noms de méthodes, outre pour des noms consacrés comme sin() pour la fonction de calcul du sinus.

Quelques exemples apparaissent à droite :

  • Remarquez le choix d'un verbe d'état comme préfixe au nom du prédicat est_pair(). Ceci permet des écritures comme if (est_pair(n)) pour un entier n donné, par exemple, qui sont naturelles
  • Dans le cas de calculer_paie(), le recours à un nom composé me paraît plus clair que calculer() ou paie(), des noms derrière lesquels les vocations des fonctions pourraient sembler opaques ou ambiguës
  • Quand un concept peut être nommé, j'en fais habituellement un type, souvent une classe. À droite, la classe Cash porte un nom significatif. Remarquez la correspondance entre les noms des méthodes et les concepts qu'elles représentent :
    • la méthode dollars() expose les dollars d'une instance de Cash, tout comme la méthode sous() expose les sous d'une instance de Cash
    • remarquez l'absence de mutateurs (de méthodes Set()), et le recours à des noms simples pour des accesseurs (dollars() plutôt que GetDollars(), par exemple)
    • la comparaison se fait naturellement avec les opérateurs == et !=
  • Enfin, le nom carre() pour le calcul du carré d'un nombre me semble clair et permet des écritures comme y = carre(x); qui, à mon avis, sont plus évidentes que ne le serait quelque chose comme y = calculer_carre(x); étant donné les us et coutumes mathématiques
bool est_pair(int);
template <class T>
   T carre(T val) {
      return val * val;
   }
class DollarsInvalides {};
class SousInvalide{};
class Cash {
   int dollars_, sous_;
   static constexpr bool est_dollars_valide(int valeur) {
      return !(valeur < 0);
   }
   static constexpr bool est_sous_valide(int valeur) {
      return !(valeur < 0 || 99 < valeur);
   }
   static constexpr int valider_dollars(int valeur) {
      return est_dollars_valides(valeur)? valeur : throw DollarsInvalide{};
   }
   static constexpr int valider_sous(int valeur) {
      return est_sous_valides(valeur)? valeur : throw SousInvalide{};
   }
public:
   int dollars() const {
      return dollars_;
   }
   int sous() const {
      return sous_;
   }
   constexpr Cash(int dollars, int sous)
      : dollars_{valider_dollars(dollars)}, sous_{valider_sous(sous)}
   {
   }
   constexpr Cash() : Cash{0, 0} {
   }
   constexpr bool operator==(const Cash &autre) const {
      return dollars() == autre.dollars() &&
             sous() == autre.sous();
   }
   constexpr bool operator!=(const Cash &autre) const {
      return !(*this == autre);
   }
   // autres membres...
};
Cash calculer_paie
   (int nb_heures, const Cash &salaire_horaire);

Types, classes, enregistrements

Pour les noms de classes, je préfère des noms reflétant des états (Carre, Forme, Couleur, pointeur_unique<T>) ou des capacités (Dessinable, Incopiable, Incompilable).

J'essaie d'éviter de la redondance dans les noms. Avec des classes, mieux vaut selon moi aller à l'essentiel. Ainsi, remarquez le choix du nom Couleur::DEFAUT plutôt que Couleur::COULEUR_DEFAUT pour la valeur par défaut d'une Couleur. Le nom de la classe me semble qualifier suffisamment le concept décrit. Notez d'ailleurs que, si plusieurs valeurs par défaut vous semblent nécessaires dans une classe, il est possible que le découpage de votre code soit perfectible.

Dans un cas où le nom d'un attribut ne compétitionne pas avec un nom de méthode (comme dans le cas de l'attribut p dans pointeur_unique<T>, à droite), je tend à omettre le suffixe _ pour garder le nom le plus naturel possible. Étonamment, je suis de plus en plus souvent capable de procéder ainsi (signe de sagesse, en vieillissant?).

Je tends à utiliser des noms débutant par une majuscule pour le code maison, destiné à des applications ponctuelles, et je tends à utiliser des noms débutant par des minuscules pour des types proches de ce qu'on peut retrouver dans les bibliothèques standards.

Évidemment, je pose un jugement de valeur quand j'écris ainsi; le code qui me semble à peu près de qualité commerciale et de nature réutilisable ressemble, à l'usage, au code des bibliothèques que nous utilisons tous de manière routinière.

Dans la même veine, mes types internes et publics collent le plus possible aux us et coutumes du standard du langage.

class Incopiable {
   // membres...
};
struct Dessinable {
   virtual void dessiner(std::ostream &) const = 0;
   virtual ~Dessinable() = default;
};
class Forme : public Dessinable {
   // membres...
};
class Couleur {
public:
   enum value_type : short {
      Rouge, Vert, Bleu
   };
private:
   value_type valeur_;
   static constexpr const value_type DEFAUT = Bleu;
   // membres...
};
class Carre : public Forme {
   Couleur couleur_
   // membres...
};
template <class T>
   class pointeur_unique : Incopiable {
   public:
      using value_type = T;
   private:
      value_type *p;
   public:
      pointeur_unique(value_type *p)
         : p{p}
      {
      }
      ~pointeur_unique() noexcept {
         delete p;
      }
      // autres membres
   };

Variables, constantes et attributs

J'ai tendance à utiliser des noms en majuscules pour les constantes connues à la compilation (et jamais pour les variables ou pour les types), mais pas nécessairement pour les constantes connues à l'exécution ou pour les paramètres const.

Vous aurez sans doute remarqué que, tel que recommandé par Boost et par messieurs Sutter et Alexandrescu dans leur livre C++ Coding Standards, je tends à apposer le suffixe _ aux noms de mes attributs. Cette écriture me semble claire et élégante, et facilite à la fois l'écriture d'initialisations à la préconstruction et celle d'accesseurs. Quand cela s'y prête, comme dans le cas du foncteur Afficher à droite, j'utilise même des noms d'attributs sans suffixe.

Je suis à l'aise avec des noms abstraits comme i ou j pour des compteurs entiers, x et y pour des nombres à virgule flottante, n pour une quantité entière ou encore p ou q pour des pointeurs. Cependant, je m'attends à ce que ces noms soient utilisés dans un espace très local (le plus localement possible), donc qu'ils aient une durée de vie très courte, à l'intérieur de laquelle leur rôle est évident. Une variable dont le rôle est plus défini ou dont la portée dépasse celle d'une fonction très générale de deux ou trois lignes mérite en général un nom plus défini.

const float PI = 3.14159f;
#include <iosfwd>
class entier {
public:
   using value_type = int;
private:
   value_type val {};
public:
   entier() = default;
   constexpr entier(value_type val) : valeur{ val } {
   }
   constexpr value_type valeur() const {
      return val;
   }
   std::ostream& operator<<
      (std::ostream&, const entier&);
};
class Afficher {
   std::ostream &os;
public:
   Afficher(std::ostream &os) : os{os} {
   }
   template <class T>
      void operator()(const T &val) {
         os << val << ' ';
      }
};
#include <iostream>
int main() {
   using namespace std;
   enum { N = 10 };
   for(int i = 0; i < N; ++i)
      cout << entier{i} << endl;
}

Jargon

Certains termes font école, en programmation comme ailleurs. Sans que je ne sois un expert de la question, voici quelques listes de termes qu'on pourrait associer à du « jargon de programmeuse » ou à du « jargon de programmeur ».

Lectures complémentaires

Quelques liens pour enrichir le propos.


Valid XHTML 1.0 Transitional

CSS Valide !