Les grands principes de nomenclature – Partie I : les classes

Je ne vais pas aller par quatre chemins : déterminer des règles de nomenclature, des naming guidelines and conventions, et surtout s’y tenir, c’est une des clés d’une bonne implémentation dans le monde fou et parfois cruel du développement informatique. Pas besoin que j’en fasse des caisses, si vous ne saisissez pas l’intérêt de la chose, je ne peux pas faire grand chose pour vous à ce stade, et c’est seulement votre expérience qui vous ramènera sur le droit chemin. Cet article va se concentrer sur les classes; de prochains articles parleront du modèle de données, du design pattern, et sera complété au fur et à mesure de ma volonté de le faire.

Classes, propriétés et méthodes

Classes

Les classes ont un nom générique, au singulier, et dont la première lettre est une majuscule. Si leur nom est composé de plusieurs mots, utilisez la convention CamelCase (liez chaque mot en mettant la première lettre de chaque mot en majuscule) pour nommer correctement votre classe. Ne séparez pas les mots par le caractère underscore « _ ». Dans la mesure du possible, préférez des noms en anglais.

quelques bons exemples de noms de classes :

  • Customer
  • Car
  • HttpRequest

quelques mauvais exemples :

  • Customers
  • car
  • Http_request

Soyons précis, ces derniers exemples ne provoqueront aucune erreur de compilation, c’est juste qu’ils sont mauvais; j’entends déjà les petits malins parmi vous qui me disent « et si ma classe ne représente pas un client mais plusieurs clients ? »; à ceux-là je répondrai d’utiliser quelque chose comme CustomerGroup. Singulier, singulier et singulier, c’est un mantra.

Quelques cas particuliers maintenant :

  • le nom de la classe est composé de plusieurs mots, dont un a une seule lettre (ex: « instrument à vent »); tout d’abord, je profite de cet exemple pour enfoncer une porte ouverte : AUCUN ACCENT !! Ensuite, les mêmes règles doivent s’appliquer ici (dans notre exemple: InstrumentAVent); On peut éventuellement, si cela conserve du sens, retirer le mot d’une lettre (tout comme les articles, d’une manière générale; et dans notre exemple : InstrumentVent)
  • le nom de la classe est composé d’un ou de plusieurs mots, dont un acronyme (HTML, XML, URL, DB, SQL, etc. parmi les plus classiques); si plusieurs mots composent le nom, on conserve l’acronyme en majuscules (URLRequest, DBConnexion, etc.); en revanche, si l’acronyme est le nom à lui seul, on ne conserve que la première lettre en majuscule (Sql,Xml, etc.); cette dernière règle est pas mal discutée, donc soyons souple sur ce sujet, mais gardons à l’esprit que, normalement, seules les constantes sont intégralement en majuscules;
  • plusieurs classes ont une partie de leur nom en commun et représentent des sous-ensembles d’un même ensemble (ex: instrument à vent et instrument à cordes, qui sont des sous-ensembles de la famille des instruments); dans ce cas, on part, dans l’ordre des mots, du plus général au plus spécifique (par ex : InstrumentVent et InstrumentCorde; autre exemple plus parlant : DBConnexion et pas ConnexionDB);
  • il ne faut pas conserver trop de mots dans les noms de classes (InstrumentDontLeSonEmaneDeLaVibrationDeCordes est un très mauvais nom);
  • il ne faut pas retirer trop de mots dans les noms de classes (Vent ou Corde ne sont pas suffisamment parlant pour des instruments); d’une manière générale, un développeur doit lire le nom d’une classe et savoir quelle réalité elle sous-tend.

Instances de classes, ou objets

Lorsque vous construisez un objet, ou instance d’une classe, la règle est toujours la même : CamelCase, mais la première lettre doit impérativement être en minuscule. D’une manière générale, il est bon, si on n’utilise qu’une seule instance de classe, de lui donner le nom de la classe (avec la première lettre en minuscule); si on en utilise plusieurs, il faut leur donner à chacun un nom explicite (dateStart et dateEnd, pas date1 et date2, par exemple). Si le langage de programmation que vous utilisez n’est pas sensible à la casse au point de ne pas pouvoir distinguer l’objet customer de la classe Customer, changez de langage de programmation, celui-là est vraiment naze.

quelques bons exemples :

  • customer = new Customer();
  • instrumentVent = new InstrumentVent();
  • dateStart, dateEnd = new Date();

quelques mauvais exemples : (encore une fois, j’enfonce quelques portes ouvertes, mais vous n’imaginez pas ce qu’on peut voir)

  • table = new Customer();
  • vent = new InstrumentVent(); (cet exemple pourrait éventuellement passer, selon le contexte – il faut qu’il n’y ait aucune confusion);
  • date1 = new Date();
  • car_i_own = new Car();

Sur les exemples avec acronymes, plusieurs possibilités selon les cas :

  1. on intègre l’acronyme : dans ce cas, il est intégralement en minuscules s’il débute le nom : urlRequest = new URLRequest();
  2. on vire l’acronyme (on peut le faire si le contexte est explicite) : connexion = new DBConnexion();

En aucun cas on ne conservera l’acronyme en majuscules, même si la première lettre est en minuscule :

  • URLRequest = new URLRequest(); // erreur de syntaxe
  • uRLRequest = new URLRequest(); //ça compile, mais c’est moche et illisible

Les préfixes et suffixes ont été à la mode, il y a quelques années; je ne vais pas en dire du mal, mais je rappelle que les épaulettes ont également été à la mode il y a quelques années (customer_s ou i_car, ça ne va vraiment pas).

Quelques exceptions, cependant :

Lorsque vous parsez un tableau d’objets (disons d’instances de la classe Customer), vous pouvez nommer chaque élément, au sein d’une boucle, avec un des préfixes suivants :

  • current (pour l’objet courant);
  • last (pour l’objet précédent);
  • next (pour l’objet suivant, s’il y en a);
  • a (pour parler de l’objet en cours, s’il est a priori indéterminé).

ex :

/*
$customers est un tableau qui contient plusieurs instances, de Customer,
ordonnées par id, mais éventuellement identiques.
Le but de ce snippet est d'afficher une seule
fois le nom de chaque client
*/
$customers = CustomerFactory::getCustomers();
$lastCustomer = new Customer();
foreach($customers as $aCustomer) {
    // $aCustomer est une instance de Customer qu'on ne connaît pas a priori
    if ($aCustomer->getId() != $lastCustomer->getId()) {
        //on ne va afficher ce $aCustomer que s'il n'est pas le même que le précédent
        $lastCustomer = $aCustomer;
        echo $aCustomer->getFullName() . "\n";
    }
}

Je vous arrête immédiatement sur le fait que ce bout de code est mal écrit et pas optimisé, j’en conviens; mais c’est le lot de tous les HelloWorld de ce monde.

Propriétés

Les propriétés doivent respecter rigoureusement les mêmes règles que les instances de classes, notamment parce qu’elles sont souvent des instances de classes.

Une petite mention, toutefois, pour les constantes; les constantes sont toujours écrites intégralement en majuscules, et la séparation entre les mots qui les composent sont du coup effectuées à l’aide du caractère underscore « _ »:

  • DATABASE_HOST = ’127.0.0.1′;
  • STATUS_INACTIVE = 5;

L’ordre des mots doit, une fois encore, refléter un ordre de contenance d’ensembles (du contenant au contenu) :

  • VEHICLE_CAR, VEHICLE_BOAT, VEHICLE_PLANE
  • HUMAN_MALE, HUMAN_FEMALE, HUMAN_GEEK

Méthodes

Les méthodes respectent également les mêmes règles que les propriétés, auxquelles s’ajoutent quelques autres conventions :

  • pour les accesseurs, sauf particularités du langage visé ( par exemple, Objective-C a des conventions propres à ce sujet), les méthodes sont nommées {get ou set} + {le-nom-de-la-propriété-avec-la-première-lettre-en-majuscule} ( name devient getName() et setName()); pour le set, le paramètre peut s’appeler exactement comme la propriété, si le langage le permet, sinon on utilisera un terme générique qu’on utilisera alors pour tous les set de toutes les classes, tel que value.
  • si une des propriétés est un booléen, on remplacera la fonction getPropriete() par isPropriete();
  • pour les autres méthodes, le nom doit refléter correctement son comportement, vu de l’extérieur, et pas sa mécanique interne (cette dernière est susceptible de changer sans affecter le reste de l’application, c’est la raison d’être de l’encapsulation); ce point est remarquablement important et je le soulèverai probablement en d’autres occasions; par exemple, getFullName() est un bon nom, getUpperCaseName() l’est beaucoup moins.
  • si une méthode renvoie un booléen, son nom doit commencer par le préfixe is, ou has, ou should, etc. en tout cas un verbe dont la réponse est oui ou non.
  • si une méthode renvoie un autre type de donnée, son nom doit commencer par get, ou retrieve, etc. en tout cas un verbe qui indique une réception.
  • si une méthode vient modifier une donnée, son nom doit commencer par set, ou update, ou change, etc. en tout cas un verbe qui indique cette idée de modification.
  • on peut affiner ces règles, mais vous avez compris le principe.

Quelques bons exemples de méthodes :

  • getId();
  • setId(id), setId(value);
  • isActive();
  • hasMoreElements();
  • containsKey(key);
  • generateUniqueReference().

Quelques mauvais exemples :

  • setName(foo);
  • getIsActive(), getActive() (dans ces deux exemples, on imagine que active est un booléen);
  • loadFromMySQL() (que se passera-t-il le jour où la mécanique interne change pour Oracle?).

Les commentaires sont fermés.