Modifier
EDIT du 24/01/2018 :
    - Modification des normes pour les variables fenêtres / états, globales et projets suite à la discussion avec Alain
    - Ajout d'une section pour ChaineConstruit

Convention de codage Je ne sais pas pour vous, mais j'ai toujours eu du mal à déterminer une convention d'écriture pour mon code WinDev®. Pourquoi ? Parce que je n'ai jamais pris le temps d'y réfléchir. Mais aussi parce que je travaille avec d'autres personnes et qu'une convention a déjà été définie.

Mais aujourd'hui, je commence à travailler sur mes propres projets et j'ai décidé de mettre en place une convention de codage. Ma propre convention, basée sur mon expérience, que ce soit avec le WLangage, mais aussi d'autres langages comme Python ou Java.

Cette convention n'est pas définitive et évoluera au fil du temps, peut-être grâce à votre avis et vos conseils. J'aurai pu la garder pour moi, mais vu que je souhaite partager avec vous du code et des projets, je vous livre aujourd'hui le résultat de mes premières réflexions.

Si à l'avenir vous utilisez mes projets, je vous invite à la lire pour mieux comprendre certains de mes choix.

WLangage - Les contraintes

Quand on est habitué à travailler avec d'autres langages, on remarque que le WLangage est vraiment à part. Des choix très particuliers ont été faits et ont un impact très fort sur les habitudes de codage. Je pense personnellement que ces contraintes sont en place pour permettre à des codeurs débutants d'appréhender beaucoup plus facilement le langage. Voici les contraintes à prendre en compte :

La langue de codage

La première chose à prendre en compte, c'est la langue. Dans WinDev, on écrit généralement en français, en tout cas, c'est ce que je fais, alors que je code en anglais en Python. C'est un choix purement arbitraire, on peut aussi coder en anglais. L'un ou l'autre, mais pas un mélange des deux. En tout cas, il faut avouer que le français facilite la vie des apprentis développeurs.

La langue française entraine une deuxième contrainte : les accents et autres lettres spécifiques au français. Dans tous les autres langages, cette question ne se pose pas, mais dans notre cas, il faut choisir. Pour ma part, je conserve les accents. J'écris en français jusqu'au bout, afin de ne pas perturber mon esprit. Je comprends plus facilement le mot téléchargé que telecharge. Je ne sais pas si c'est le verbe conjugué ou le participe passé.

Les majuscules / minuscles

C'est une contrainte dont je me serai bien passé. Ce n'est pas pratique. J'aime écrire les noms des classes avec une lettre majuscule et le nom des variables avec une lettre minuscule. Bilan des courses : la classe Client et la variable client, et bien, ça ne passe pas. La convention que je vais vous proposer essaye de tenir compte de cela.

Beaucoup de mots réservés

Dans la plupart des langages, je pense qu'on a entre 20 et 50 mots réservés. La belle affaire, on peut se passer de ces mots. En WLangage, on a aussi des mots réservés (RETOUR ou RENVOYER par exemple), mais on a aussi une quantité phénoménale de fonctions qui sont chargées en permanence. On doit tenir compte de ces fonctions dans notre convention de codage, sinon, on risque d'avoir des warnings disant que tel mot est réservé. De plus, la coloration syntaxique a parfois du mal avec ces mots réservés. Ceux qui m'embêtent le plus, ce sont les fonctions Erreur et Message que je ne peux pas utiliser comme je le souhaite.

Convention de langage dans mes projets

J'ai décidé de coder en français et d'utiliser les accents. Cela me pose un seul problème, c'est lorsque j'utilise la fonction VariantVersJson, les accents sont pris en compte. Mais j'ai appris à vivre avec.

Voyons maintenant les règles que j'applique aux différents éléments des projets :

Les dépendances

WinDev ne facilite pas les choses et il est très facile de créer des dépendances entre chaque élément. Ma convention impose de réduire au maximum les dépendances ou de les afficher explicitement.

La complétion automatique

La complétion automatique est très importante (Merci Alain d'avoir souligné ce point) et la convention de nommage doit permettre de l'utiliser voire de faciliter l'utilisation de la complétion.

Les fonctions

Je vais en choquer plus d'un, j'ai décidé que les noms de mes fonctions ne seraient pas en CamelCase mais en snake_case. J'aurai préféré le CamelCase, mais vu que tout ce qui est livré avec WinDev est en CamelCase, le snake_case m'offre un espace de nommage non utilisé. De plus, toutes mes fonctions sont en minuscules, sauf si la majuscule peut être utile, comme pour gérer un acronyme (et encore...) J'ai choisi aussi de mettre les verbes d'actions à l'infinitif plutôt qu'à l'impératif. L'ordinateur, c'est pas une personne, je lui donne pas d'ordre, moi...

Au niveau de la portée des fonctions, j'ai fait le choix suivant :

  • procédures globales : elles sont préfixées du nom de la collection de procédure
  • procédures locales : pas de préfixes
  • procédures internes : elles sont préfixées par _

Enfin, je ne me pose pas de question, j'utilise toujours le mot clé PROCEDURE pour mes procédures. Je n'utilise jamais le mot FONCTION et d'ailleurs, les fonctions internes n'existent pas. Merci Jack pour m'avoir fait remarqué ça et désolé, mais je n'ai pas envie de me prendre la tête avec ces mots clés ;)

Exemples :

  • afficher_table au lieu de AfficheTable
  • MaCollection.supprimer_fichier au lieu de gSupprimeFichier(de quelle collection ?)
  • i_détecter_majuscule au lieu de iDétecterMajuscule

`En ce qui concerne le code, mes procédures ne doivent pas faire plus d'une trentaine de lignes. Si c'est le cas, j'essaye d'extraire des fonctions globales ou locales et si ce n'est pas possible, je les découpe en procédures internes.

Les variables

J'ai choisi le même principe que les fonctions pour nommer mes variables, le snake_case. Et surtout, je n'utilise pas la notation hongroise. Je fais en sorte que la définition de ma variable et son utilisation ne soit pas trop éloignées. En contrepartie, je fais des noms de variables très explicites, quitte à ce que le nom soit un peu long. Je préfère connaître l'objectif de ma variable que la forme de ce qu'elle contient.

Pour les tableaux, en général, je mets la variable au pluriel. C'est suffisant.

Exemples :

  • chemin_fichier_paramètre au lieu de sCheminFichierParamètre
  • date_courante au lieu de dDateCourante
  • articles au lieu de tArticles

Les variables dans les procédures internes

En parcourant le blog du support technique, j'ai constaté qu'ils préfixaient les variables par multiplié par le niveau de la procédure interne. J'ai trouvé l'idée excellente. Donc, si j'ai une procédure interne qui en contient une autre, j'aurai une variable commençant par dans la première et par __ dans la seconde. Cela permet de réutiliser le même nom de variable à chaque niveau et j'évite ainsi d'utiliser mes variables de premier niveau comme des variables globales.

Les variables dans les méthodes de classe

Je préfixe toujours mes membres par :. Cela me permet de les distinguer des variables locales. En fonction de la portée de la variable, je préfixe le nom aussi avec un _ (membre protégé) ou __ (membre privé).

Exemples :

  • :un_membre_public
  • :_un_membre_protégé
  • :__un_membre_privé

Les paramètres

Je n'utilise aucun préfixe pour distinguer un paramètre d'une variable. Par contre, je type toujours (sauf exception) mes paramètres (mais je retire le est qui n'est pas obligatoire).

Et si je suis dans une procédure interne, je préfixe par _.

PROCEDURE afficher_valeur(valeur_affichée est entier)

Les variables globales fenêtres (ainsi que les états)

Lorsque j'ai besoin d'une variable fenêtre (ou état), je les intègre dans une structure et je déclare une variable vl (variable locale) du type de cette structure. Cela me permet de le distinguer des variables de mes procédures et de pouvoir utiliser la complétion automatique.

Avec cette méthode, si j'ai besoin, je peux sérialiser / dé-sérialiser les variables de ma fenêtre, ce qui peut-être pratique pour des tests.

PROCEDURE MaFenêtre(codes_article_sélectionnés est tableau de chaines)
GLOBAL
stVariablesFenêtres est une structure
    titre_fenêtre est une chaine
    codes_article_sélectionnés est est tableau de chaines
FIN

vl est stVariablesFenêtres

LOCAL
vl.titre_fenêtre = MaFenêtre..titre
vl.codes_article_sélectionnés = codes_article_sélectionnés

Les variables globales de collection de procédures

J'utilise aussi une structure. Il faut faire attention dans le nom de la structure, car WinDev signale les doublons si on utilise un même nom de structure dans deux collections. Pour cela, je suffixe le nom de ma structure par le nom de ma collection.

Et pour le nom, j'ai choisi vg (variable globale).

Je peux sérialiser / dé-sérialiser mes variables, ce qui le rend très utile pour les tests unitaires.

Enfin, cela m'oblige à préfixer du nom de la collection car WinDev me signalera une anomalie sur l'utilisation de vg.

Par exemple, dans une collection de procédures nommée Collection1

stVariablesCollection1 est une structure
    chemin_paramètres est une chaine
FIN

vg est stVariablesCollection1

Et pour l'utilisation :

Collection1.vg.chemin_paramètres = "..."

Les variables globales projets

J'essaye de ne plus mettre de variables globales au niveau du projet. Si je dois en avoir, je les mets dans la collection de procédures adéquate, quitte à en créer une si besoin.

Et si je dois vraiment en avoir, j'utilise le même système que pour les fenêtres et les collections de procédures mais j'utilise une variable que je nomme variable_projet pour que l'utilisateur sente le malaise d'utiliser une telle variable.

Les constantes

Dans la plupart des langages, les constantes sont en majuscule. Mais vu les contraintes sur les majuscules vues plus haut, on ne peut pas se servir de cette notion. J'utilise donc la notation _SNAKECASE. Elle me permet de distinguer les constantes des variables. C'est très pratique.

Exemples :

CONSTANTE
    _LANGUE_ = "fr"
    _PI_ = 3.14
    _NOMBRE_HEURES_JOURNEE_ = 24
FIN

J'utilise aussi cette notation pour les énumérations ou les combinaisons (pour moi, ce sont aussi des constantes).

Enfin, j'applique aussi cette règle à certaines variables que je considère comme des constantes, mais que je ne peux pas initialiser comme des constantes. Dans ce cas, j'utilise le mot clé Soit.

Exemple :

Soit _CHEMIN_FICHIER_PARAMETRE_ = fRepExe() + ["\"] + "paramètre.ini"

Les classes

Dans le cas des classes, j'utilise la convention CamelCase en faisant bien attention de ne pas tomber sur un mot clé du WLangage.

Par contre, j'hésite à les préfixer par un c. Ce préfixe permettrait de bien les distinguer, mais si on veut ajouter d'autres préfixes pour regrouper les classes ensembles, cela alourdit le nommage. C'est donc toujours à l'étude.

Exemples :

  • Véhicule ou cVéhicule
  • VéhiculeAvecMoteur ou cVéhiculeAvecMoteur

Les collections de procédures

Idem que les classes. Sauf qu'en général, c'est un seul mot, celui qui correspond à la thématique de la collection de procédures. Et si je dois utiliser un préfixe, je mets cp. (encore à l'étude aussi). Mais cela me gène car une de mes collections de procédure s'appelle yLogger, et j'aime l'idée de conserver ce nom ainsi.

  • Finance ou cpFinance
  • InterfaceGraphique ou cpInterfaceGraphique
  • yLogger ou cpLogger ou cpyLogger

Les fenêtres

Pour les fenêtres, j'utilise CamelCase et je les préfixes par fen pour les fenêtres standards et fin pour les fenêtres internes.

Exemples :

  • fenMenuPrincipal
  • finEditionClient

Les champs

Idem que les fenêtres, j'utilise CamelCase et j'utilise un préfixe en fonction du champ.

Exemples :

  • btRafraichir
  • csNomClient
  • taClients
  • cbPays

Les commentaires

J'utilise très peu de commentaires.

Je préfère mettre en place un code auto-documenté qu'un code avec beaucoup de commentaires. Pourquoi ? Tout simplement parce que les commentaires périment et il est très compliqué de les enlever lorsqu'on nettoie le code.

Pour commenter mon code, j'utilise des noms de fonctions explicites et s'il le faut, je découpe un code trop complexe avec des procédures internes. Et si je dois expliquer un calcul ou une condition, je passe par une ou plusieurs variables intermédiaires. Effectivement, je perds en performance (mais si peu) mais je gagne en lisibilité.

Le seul cas où j'utilise un commentaire, c'est pour expliquer le pourquoi d'un code (le code explique le comment mais pas le pourquoi) ou pour documenter mes fonctions (j'utilise pour cela les automatismes de WinDev).

ChaineConstruit

Ce point va peut-etre évoluer au fil du temps, mais j'utilise la fonction ChaineConstruit d'une manière particulière en utilisant une variable de type procédure déclarée au niveau du projet que je nomme _ qui la remplace.

Je trouve que ça permet de mettre en valeur les chaînes et ça permet de faciliter la traduction de mes applications.

Résumé

Dans cet article, nous avons vu les différentes conventions que j'utilise pour nommer les procédures, les variables et les autres éléments dans mes projets WinDev.

Conclusion

J'espère que cette réflexion sur les conventions de codage vous sera utile. Bien entendu, il y a beaucoup de points que je n'ai pas abordé (l'analyse par exemple) car je n'ai encore rien défini.

N'hésitez pas à donner votre avis et vos conseils via les commentaires. Peut-être qu'un jour, on arrivera à faire comme Python et mettre au point une convention de codage standard et facile d'utilisation malgré les contraintes imposées par WinDev.

Quoiqu'il en soit, c'est celle-ci que j'utilise dans les projets liés à mon blog. Le premier d'entre eux est yLogger et petit à petit, j'adapte le code de ce projet avec ces conventions.

N'hésitez pas à vous abonner si vous souhaitez être tenu au courant de l'actualité de mon blog.

Je vous remercie pour votre lecture.

Article précédent Article suivant