Publié par
Il y a 5 années · 6 minutes · Craft

Craftsman Recipes – De l’art de bien nommer les concepts

On sous-estime souvent l’importance du nommage. Un mauvais nommage peut ruiner la compréhension d’un programme, tandis qu’un bon nommage peut à l’inverse le rendre aussi lisible que du français (ou de l’anglais, selon la langue dans laquelle vous codez). Ce premier article de la série Craftsman Recipes se focalise sur des pratiques de nommage en langage objet.

Quelques conseils

Un nom doit représenter toutes les facettes visibles – c’est-à-dire utiles au client – du concept nommé. Si vous ne parvenez pas à trouver un nom assez court pour un type, c’est probablement qu’il a trop de responsabilités (violation du SRP, principe de responsabilité unique) et qu’il est temps de le séparer en plusieurs concepts.

De même, une méthode devra porter un nom exprimant clairement toutes ses responsabilités, à son niveau d’abstraction. Cette méthode appellera elle-même des sous-méthodes expliquant clairement ce qu’elles font à un niveau plus bas d’abstraction. Par exemple : une méthode getAccount() qui créerait le compte en question pour le cas où il n’existerait pas encore, devrait s’appeler getOrCreateAccount(). Si créer le compte nécessite de récupérer des données en base, le nom de la méthode ne le montrera pas, mais celui d’une de ses sous-méthodes le fera :

 public Thing getOrCreateAccount() {
  if (this.account == null) {
   this.account = createAccount();
  }
  return this.account;
 }

 private Thing createAccount() {
  // ...
  someData = fetchSomeData();
  someOtherData = getSomeOtherData();
  // ...
  return new Account(someData, /* … */);
 }

Rappelons en passant qu’une méthode est un message envoyé à un objet. Il s’agit donc le plus souvent d’un ordre et il convient donc dans ce cas de nommer la méthode avec un verbe à l’impératif (à l’inverse, la documentation est descriptive et il faut donc utiliser l’indicatif, à la troisième personne). Dans certains cas, une méthode retourne juste une propriété, et on pourra alors lui donner le nom de cette propriété (par exemple : Enum.name()).

Dans le cas du nommage d’une variable (locale, membre ou paramètre) on cherchera à exprimer le rôle de cette variable dans son contexte, plutôt que de lui donner le nom de son type. Ainsi, lors de l’envoi d’un email, un User sera peut-être un sender, ou un receiver.

Un nom long n’est pas un problème s’il clarifie le code. À l’inverse, on pourra se permettre des noms d’autant plus courts que la portée du concept nommé est limitée. Une méthode de trois lignes travaillant avec deux variables pourra nommer ces variables avec une unique lettre sans entraîner de problèmes de lisibilité. De même, il est communément admis et donc compris qu’une variable d’itération soit nommée ij ou k.

Doit-on écrire son code en anglais ou en français ? Cela dépend du contexte : le code est-il susceptible d’être ouvert à un développement plus international ? Si la réponse est non, quel est le niveau de maîtrise de la langue de Shakespeare dans l’équipe ? Ce qui compte au final est que la langue reste la même dans tout le code, et que ce code soit compréhensible.

Enfin, s’il est nécessaire d’écrire un commentaire pour comprendre ce que fait une portion de code, cela indique très probablement qu’il est temps de revoir le nom des concepts manipulés (ainsi que la granularité de ce qui est fait dans la portion de code, ce point sera détaillé dans un autre article). Une fois cela corrigé, on pourra supprimer les commentaires (sauf en cas de documentation d’API publique bien sûr).

À éviter

Les points suivants ne sont pas des règles absolues, mais montrent très souvent un problème de nommage.

Les suffixes en "er" : Manager, Handler, Helper, et parfois Controller. Ces suffixes traduisent souvent le besoin de placer quelque part du comportement utilisé en plusieurs endroits de l’application, comportement en rapport avec le concept exprimé par le préfixe de la classe : un UserHelper contiendra donc du comportement relatif au type User. Et bien souvent le type User est une structure de données anémique, sans aucun comportement. Bref, nous sommes en face d’un code procédural et non objet. L’idée est donc de remettre le comportement là où il doit se trouver, à savoir pour notre exemple au sein de la classe User.

Les suffixes en "able" : ils traduisent en général un manque d’inspiration lors du nommage : "on peut itérer dessus ? Appelons-le Iterable !". Peut-être Iterable aurait-il pu s’appeler Séquence ou Stream ? Un Runnable n’est-il pas seulement une Action, ou une Activité ? En bref : ce n’est pas parce que l’on peut marteler un Clou que l’on va l’appeler Martelable (exemples empruntés à Carlo Pescio dans un article que je vous invite à lire).

Le préfixe "i" pour les interfaces : cette notation traduit souvent la création d’une interface à partir d’une classe, a posteriori, et sans autre implémentation que la classe d’origine. Tout d’abord, ce "i" rigidifie le code : quid des clients de cette interface si je désire la changer en classe abstraite ? Dans le pire des cas le nom reste, dans le meilleur il faut changer beaucoup de code (heureusement, l’IDE fait ça très bien). Plus grave : l’interface devrait porter le nom d’un concept utile à ses clients, et les implémentations de cette interface devraient porter un nom représentant la particularité de l’implémentation. Par exemple, peut-être qu’une interface extraite à partir d’un type concret Voiture représentera en fait un Véhicule, auquel cas il sera dommageable de l’appeler IVoiture.
On évitera également le processus inverse, consistant à nommer la première implémentation d’une interface à partir du nom de l’interface et du suffixe "Impl".

D’une manière générale, évitons toute forme de notation hongroise. Avec un langage statiquement typé tel que Java, nous n’avons jamais à chercher bien loin le type d’une variable ; inutile donc de la préfixer par "int", "str" ou autre. Et quelque soit le langage, le type d’une variable devrait pouvoir se déduire de son rôle. De même, il est bien rare de coder sans un IDE ; il n’est donc pas nécessaire de préfixer ses variables membres par "m" (member) ou "f" (field). Si l’on se tient à des méthodes de taille raisonnable, il est également inutile de préfixer ses paramètres d’un "p".

Conclusion

Vous l’aurez compris, passer 5 minutes à chercher un nom pour un concept n’est pas une perte de temps ! De vos noms dépend la cohérence du système, alors pensez-y avant de commiter votre prochain IManagerFactoryHelper ;-)

Note finale : puisqu’on parle de nommage, notons que le mot historique correct en français pour l’action de nommer quelque chose est "dénomination". Mais bon, "nommage" est entré dans le dictionnaire "grâce" à nous, les informaticiens.

Nicolas Demengel
Nicolas est arrivé chez Xebia en 2010 et a 5 ans d'expérience sur le développement d'applications Java/Web.

Nicolas est un développeur passionné et pragmatique. Il aime la technologie mais n'oublie pas que la finalité d'un produit, c'est le service rendu à l'utilisateur.
Son credo : un produit doit se construire et prouver sa qualité avec du feedback et des tests !

Pour cette raison, Nicolas travaille également sur le plug-in MoreUnit pour Eclipse.

11 réflexions au sujet de « Craftsman Recipes – De l’art de bien nommer les concepts »

  1. Publié par Gautier, Il y a 5 années

    Super, une série de craftsman recipes, hâte de voir la suite !!!

    Attention cependant, la notation hongroise ne consiste pas à préfixer par le type de la variable, ça c’est un mauvais emploi de cette notation. Voir le premier commentaire du lien ci-dessous pour plus d’informations
    http://stackoverflow.com/questions/111933/why-shouldnt-i-use-hungarian-notation

    Cela dit, il est vrai qu’il rarement intéressant d’utiliser cette notation en Java.

    De manière générale, il faut toujours se fier à l’existant pour décider comment bien nommer les concepts. Le code doit rester cohérent.
    Et s’il n’y a pas d’existant, alors consulter les recommandations et le code source de projets utilisant les mêmes technologies.
    On a la chance d’avoir accès au code source de plusieurs projets facilement, autant en profiter.
    Ainsi, si l’on fait par exemple du développement Android (en Java), il ne faudra pas suivre certains points de votre article mais bien préfixer certaines variables avec m pour member, s pour static non final etc)

    Enfin, et ça c’est plus subjectif, votre idée d’éventuellement employer le français lors de l’écriture du code source est très tolérante, mais je ne suis pas trop de cet avis.
    Certains métiers imposent l’emploi d’une certaine langue (notamment chez les dresseurs d’animaux).
    Chez les « dresseurs de code », l’emploi de l’anglais devrait être imposé (sauf si on ne peux vraiment pas faire autrement évidemment).
    Ça devrait faire parti de notre métier de connaître cette langue, et cela nous aidera forcément à un moment où à un autre, lorsqu’on sera bloqué, et que la seule documentation disponible pour nous aider sera en anglais.
    Essayer d’écrire en anglais dans tous les cas permettra de s’améliorer et ça nous aidera toujours sur le long terme.
    Il faut toujours penser au futur, votre projet risque d’avoir du succès et vous pouvez être amené à employer des développeurs étrangers un jour ou l’autre ? Qui sait ?
    Ne faites pas la même erreur qu’OpenOffice à avoir écrit la plupart de ses commentaires en allemand !!!

    Rien à redire sur les autres points, je fais souvent l’erreur de préfixer inutilement mes interfaces avec un I, promis demain j’arrête !

  2. Publié par Nicolas Demengel, Il y a 5 années

    En effet, la notation hongroise est apparue pour des raisons sémantiques. Je me suis un peu trop focalisé sur son usage le plus courant, propageant ainsi le malentendu. Toutes mes excuses à Charles Simonyi :-)

    Pour ce qui est de l’usage du français, je suis à titre personnel plutôt contre (d’autant qu’on en arrive à des mélanges imbitables quand on respecte par ailleurs d’autres conventions tel que JavaBean : getMaChose()). Mais force est de constater que sur de nombreux projets français, le niveau d’anglais est tellement bas que le code en est définitivement incompréhensible. Alors si au final le développement est destiné à rester en France – ce qui est en général impossible à prévoir avec certitude – pourquoi pas…

  3. Publié par Cédric, Il y a 5 années

    Sympa cet article, il reprend bien les bonnes pratiques de nommage que je m’efforce d’appliquer au quotidien.

    Cependant je rejoins l’avis de Gautier au niveau de l’utilisation du français. Ça fait deux ou trois projets sur lesquels je passe et ou la consigne est « codez en français, commentaires et javadoc en français »
    Les commentaires et la javadoc je n’ai rien a ajouté, mais le code en français est une hérésie (je m’emporte un peu) et j’en ai un parfait exemple sous les yeux : La méthode commence plutôt bien, javadoc en français, elle est bien nommée, ses paramètres sont bien nommés. On commence le traitement, puis un petit commentaire (surement inutile) en français.
    Puis la d’un coup, c’est le drame, on appelle une librairie dont les méthodes sont en anglais (ben oui c’est normal… Tout le monde code en anglais) donc les résultats de ces méthodes sont stockés dans des variables nommées de la même façon donc en anglais, des méthodes ont été extraites pour manipuler ces variables (peut être pas par le même développeur) donc nommées en anglais aussi…

    On se retrouve avec un imbroglio des deux langues et c’est du grand n’importe quoi.

    Pour moi on pourra coder en français(allemand etc…) le jour où les librairies/frameworks qu’on utilisera seront codées dans la même langue (probablement jamais).

    J’ai hâte de voir les autres articles de la série

  4. Publié par Olivier, Il y a 5 années

    Bonjour,

    article intéressant mais je ne suis globalement pas du tout d’accord avec la partie sur les nommages en -able et en -er.

    Je ne comprends pas bien le procès qui est fait par exemple à Runnable.

    Runnable renvoie au concept de Thread qui est un concept technique d’assez bas niveau finalement. Quand on lit ou relit la Javadoc de thread on se rend bien compte que les concepts associés (Runnable, …) sont de l’ordre d’un choix d’implémentation technique.

    Cela n’a rien à voir avec un concept métier. Le problème dans votre exemple est d’utiliser un concept très lié à une implémentation technique pour modéliser un concept métier « Activity » ou applicatif.

    Ensuite, sur les nommages -er, vous indiquez « souvent le besoin de placer quelque part du comportement utilisé en plusieurs endroits de l’application ».

    Je suis d’accord sur les Helper mais pour le reste … bcp de librairies reconnues pour leur qualité utilisent ces nommages sans pour autant être mal conçues d’un point de objet.

    Je trouve ces affirmations assez gratuites.

    Cdlt.

  5. Publié par Nicolas Demengel, Il y a 5 années

    Merci pour votre commentaire Olivier,

    Je me permets d’insister sur le fait que les points proposés « ne sont pas des règles absolues, mais montrent très souvent un problème de nommage. » Il s’agit bien là de s’interroger au cas par cas sur la pertinence d’un nom, et non pas de faire une condamnation systématique de l’utilisation d’un suffixe particulier, même si certains sont symptomatiques.

    À titre d’exemple personnel, bien que fermement convaincu par l’importance des noms, je pèche souvent en recourant à un terme tel que « Provider » là ou autre chose aurait été (plus ?) approprié (exemple : « TimeProvider » vs « Clock »).

    Par ailleurs, je ne comprends pas vos arguments concernant « Runnable » : en quoi un aspect technique devrait-il être moins sujet à une recherche de nom qu’un aspect métier ? Et pourquoi Activity serait-il un terme plus métier que technique ? Si vous deviez expliquer votre code à quelqu’un en français, diriez-vous vraiment que telle ou telle activité donnée à un Thread est un « Lançable » ?

    Enfin, une librairie de qualité peut également se retrouver à cours d’inspiration pour un nom ;-) Et certains noms peuvent se contenter de nuire à la clarté du code, sans pour autant remettre en question toute la conception objet.

  6. Publié par Olivier, Il y a 5 années

    Bonjour,

    je me permets de répondre à votre question :

    « en quoi un aspect technique devrait-il être moins sujet à une recherche de nom qu’un aspect métier ? »

    En rien ! Mais pour montrer l’inanité du nommage de l’interface Runnable vous associez cette interface dans une modélisation d’un concept qui émerge d’une application. Or l’interface Runnable fait référence à un contexte très particulier dans Java. Cette interface n’a pas été faite pour modéliser une activité au sens large.

    Pour bien clarifier votre pensée il serait intéressant que vous développiez un peu ce cas d’utilisation (quel est le besoin ? Métier ou technique ?).

    Sinon, j’ai lu quelques posts de Carlo Pescio notamment sur les contrôleurs car iul faut avouer que l’article que vous donnez à en référence est assez culoté :

    « I’m not going to say much about Controller, because it’s so popular today (MVC rulez :-) that it would take forever to clean this mess. Sad sad sad.
     »

    Bref, il ne va rien dire parce que ce serait trop long mais c’est vraiment mal.
    Pas très convaincant comme démonstration.
    Mais il a cependant développé son idée en détail dans un autre article : http://www.carlopescio.com/2012/03/life-without-controller-case-1.html

    Le problème de sa démonstration sur le modèle de la pompe c’est qu’il fait porter du comportement métier au contrôleur alors que le pattern MVC ne va pas dans ce sens. Il peut ensuite sans problème supprimer le contrôleur.
    Personnellement j’utilise le pattern MVC pour gérer l’interaction entre des composants JSFs et le modèle, et je ne vois pas très bien comment se passer du contrôleur ici.

    Cdlt.

  7. Publié par tibo, Il y a 5 années

    Bonjour,

    Je ne suis pas du tout d’accord avec le proces qui est fait, notamment au suffix en -able.
    Certes ca manque d’imagination, mais quel le but? De faire en sorte que ce soit clair. Je vois un objet qui implemente Iterable et Closeable : Je comprend dessuite ce que ca veut dire. J’ai implemente des classes avec a chaque fois une methode destroy : Je regarde si l’interface Destroyable existe.
    Je trouve completement absurde de bannir les -able sous pretexte que ca manque d’inspiration! Et oui je trouve que Hammerable et un bon nom d’interface si on a plusieurs objets qui ont la capacite de se faire marteles.

  8. Publié par Nicolas Demengel, Il y a 5 années

    Le problème que je vois _la plupart du temps_ avec les noms en -able est la manière dont ils sortent de nos esprits : on prend la ou les méthodes que nous avons mis(es) dans le type et on ajoute -able. Or, il existe souvent un nom de concept adéquat, et je trouve dommage que nous ne l’utilisions pas. Le résultat est compréhensible, mais le vocabulaire s’éloigne du langage humain plus que nécessaire (encore une fois, dans le cas où un concept adéquat est identifiable).

    D’une manière générale, il est possible de trouver un autre nom que *-able. Le problème est qu’il est difficile d’y réfléchir une fois qu’un tel nom a déjà été donné. Sur les types fournis par Java, je comprends que le besoin d’avoir un nom très abstrait ne facilite pas la tâche, mais nous n’avons pas nécessairement besoin d’avoir ce même niveau d’abstraction dans notre propre code.
    Et même dans le JDK, on trouve des noms tels que Appendable, qui ne traduit absolument pas le fait que l’on peut lui ajouter uniquement des char ou CharSequence. Si j’en crois sa Javadoc, il aurait tout aussi bien pu s’appeler CharReceiver ou CharProcessor (oui, je fais ici un double combo : -er et -or). De même pour Closeable : d’après la définition de sa Javadoc, il aurait pu s’appeler Resource (qui est pris à présent, dans javax.annotation).
    Cela étant je comprends bien que l’on désire parfois mettre en évidence l’importance d’une méthode pour un aspect plus technique, comme c’est le cas pour Closeable ou le Destroyable de tibo : on peut faire beaucoup de suppositions sur un type nommé Resource, moins sur un type nommé Closeable. Pour autant, une Activity me paraîtra toujours clairement être quelque chose que l’on peut donner à exécuter, et je préfère donc ce terme à Runnable.

    Pour ce qui est de Controller, le problème que je vois est que ce terme n’est pas seulement utilisé dans le cadre du pattern MVC, et que même dans ce cadre on va parfois y voir l’implémentation complète d’une logique faisant l’économie d’un vrai modèle objet. Il ne s’agit pas de remettre en cause le pattern MVC.

    Bref, vous l’aurez compris, les noms pointés du doigt par cet article ne sont que des indices que quelque chose peut aller de travers. Il convient de vérifier si cela est le cas, comme le suggèrent les explications de chaque paragraphe. Je ne pense pas nécessairement que tous les adjectifs en -able ou les noms en -er soient à bannir. Une fois supprimés les abus flagrants, les noms restants peuvent être parfaitement accept-ables ;-)

    In fine, je le répète et vous rejoins sur ce point : ce qui compte est que le programme soit compréhensible.

    PS : pour ce qui est de Carlo Pescio, ses articles sont toujours – volontairement ? – provocateurs. Je trouve personnellement qu’ils permettent ainsi de faire avancer la réflexion, mais je n’irais pas jusqu’à défendre leurs travers. Je continue tout de même à conseiller leur lecture.

    PS 2 : @Olivier, qu’entendez-vous par « activité au sens large » ? Pour clarifier ma pensée, je définirais une activité comme étant quelque chose qui peut être en état actif, qui exécute une fonction. À ce titre, cela me parait coller avec ce que fait un Runnable et me semble également compatible avec sa javadoc.

  9. Publié par Michel Perfetti, Il y a 5 années

    il ne faut pas aussi négliger l’homogénéité du code. si le nommage n’est pas optimal mais homogène, mieux vaut le suivre à mon avis. Après chaque langage, chaque framework ses conventions. De mon coté en C#, j’impose les choses suivantes en gros:
    * IFoo pour les interfaces pour suivre la convention du framework,
    * PascalCase pour les noms de classes, méthodes, propriétés,
    * camlCase pour les variables, paramètres
    * _camlCase pour les champs privés.
    * VerbeAction pour les méthodes
    * Pas de verbe pour les propriétés (avec une petite exception pour Is: IsValid, IsSelected…)
    * Toujours du dev en anglais: je trouve que c’est plus concis

    Le nommage des namespace est aussi un sujet brulant, qui mériterai un billet à part entière :)

  10. Publié par Johann, Il y a 5 années

    Hello,

    merci pour cet article intéressant.

    Il n’est pas sans me rappeler un livre de Robert C. Martin du nom de Clean Code, dont un chapitre est dédié aux bonnes pratiques sur le nommage. Je ne touche pas de royalties, mais je conseil aux personnes intéressées par ce sujet de lire ce livre :)

  11. Publié par Stephan, Il y a 5 années

    Je suis globalement d’accord avec les recettes proposées dans ce billet. Néanmoins, j’apporte un bémol sur la langue à utiliser dans le code.
    J’ai codé sur plusieurs types de projet, en anglais ou en français, et généralement la bon compromis se retrouve sur du franglais…
    C’est généralement du pragmatisme que j’utilise, à savoir tout ce qui est API reste en anglais. Mais pour le domaine fonctionnel les traductions sont souvent difficiles, surtout si celui-ci est complexe ou spécifique. Surtout quand on doit parler aux fonctionnels coté métier, si ce client est français bien sûr!
    En résumé, je préfère avoir une méthode isEnAlternance() sur une classe Formation que isSandwichCourse sur une classe Training… Sans parler des faux amis ou termes similaires ou difficiles à traduire!

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *