Privilégier la charte de programmation livrée en standard par PCSOFT®. Elle est acceptée et utilisée par la majorité des développeurs en WLangage®.
La convention de nommage et le préfixage permet de connaître rapidement le type d'éléments, ainsi que le type de variable et sa portée.
// Quelques exemples :
sNom // Le préfixe s correspond à une variable de type chaîne
nAge // Le préfixe n correspond à une variable de type entier
rTVA // Le préfixe r correspond à une variable de type réel
dNaissance // Le préfixe d correspond à une variable de type date
FEN_TABLE_SOCIETE // Le préfixe FEN_ correspond à un élément de type fenêtre
gtaPersonnel // Le préfixe gta correspond à une variable de type tableau associatif avec une portée globale
gm_sCheminFicherDll // Le préfixe gm_s correspond à un membre d'une classe de type chaîne avec une portée globale
Nommer les éléments de projet de manière simple et explicite dans l'"esprit" de la nomenclature PCSOFT®.
Si la langue du code est en français alors utiliser des noms en français pour tous les éléments du projet (fenêtres, états, champs, variables, procédures, classes, ...).
Typer les valeurs de retour de toutes les procédures.
Privilégier les procédures internes si leur exécution n'est faite que depuis un seul code appelant.
Préfixer les appels aux procédures globales par le nom de la collection de procédures.
Rédiger le nom des fonctions avec un verbe à l'infinitif. L'objectif est que le nom de la fonction puisse donner suffisamment d'informations pour éviter d'aller lire le code. Une fonction doit faire une seule chose. Son code idéal doit être séquentiel, lisible de haut en bas. Le nombre de ses paramètres doit être réduit (moins de 4).
Utiliser les types de variables énumérations et combinaisons dans les paramètres de procédures. Ceci afin d'éviter des erreurs et de faciliter la lecture, la programmation et l'appel à ces procédures.
Nom de fenêtre dans l'appel d'une procédure :
UneProcédure("NomFenêtre")
Doit s’écrire :
UneProcédure(NomFenêtre..Nom)
S'il y a un renommage du nom de la fenêtre, l'éditeur changera automatiquement le nom dans l’appel de la procédure alors que dans le premier code, le développeur doit penser à modifier le nom de la fenêtre entre guillemets.
Ne pas oublier l'attributs d'extension dans la déclaration de la procédure. Ceci afin de faciliter l'appel à la procédure :
PROCÉDURE UneProcédure(LOCALE sNomFenêtre est une chaîne <nom de fenêtre>)
PROCÉDURE UneProcédure(LOCALE sNomEtat est une chaîne <nom d'état>)
Typer toutes les variables selon leur contenu. Ne pas oublier également les paramètres des procédures.
Eviter la négation dans le nom d'une variable de type booléen.
Limiter le nombre de variables globales.
Nommer les constantes en majuscules.
Préfixer les utilisations des variables globales par le nom de la collection de procédures.
Éviter d'utiliser l'instruction SORTIR dans les boucles. Ceci afin d'améliorer la lisibilité du code et visualiser rapidement toutes les conditions de sortie de la boucle.
Remplacer un couple (POUR , SORTIR) par une boucle TANTQUE.
A titre d'exemple, le premier code ci-dessous peut être remplacé par le deuxième code :
tabTP est un tableau de TâcheParallèle
bSortir est un booléen = Faux
TANTQUE PAS bSortir
bSortir = Vrai
POUR i = tabTP..Occurrence _À_ 1 _PAS_ -1
SI PAS tabTP[i]..Terminée ALORS
bSortir = Faux
Temporisation(25, tempoSourisEtClavier)
SORTIR
FIN
FIN
FIN
tabTP est un tableau de TâcheParallèle
nIndice est un entier = 1
TANTQUE (nIndice = 1 À tabTP..Occurrence)
SI tabTP[nIndice]..Terminée ALORS
nIndice++
SINON
Temporisation(25, tempoSourisEtClavier)
FIN
FIN
Dans une instruction SELON, toujours ajouter le mot-clé AUTRE CAS à la fin.
Et si vous êtes sûr qu'il n'y aura aucun passage dans ce traitement alors y ajouter un point d'arrêt par programmation avec le mot-réservé STOP .
Cela permet de s'arrêter lors du débogage du projet; et pourquoi pas d'ajuster l'instruction SELON en conséquence.
SELON nAge
CAS 0 À 18 : Trace("Mineur")
CAS 19 À 150 : Trace("Majeur")
AUTRE CAS :
STOP // Ne devrait jamais s'arrêter ici
FIN
En WLangage®, il existe plusieurs fonctions qui renvoient un résultat identique. N'utiliser qu'une seule et uniquement qu'une seule de ces fonctions.
Le résultat de ces fonctions sont identiques mais une recherche dans le code source obligerait et pénaliserait les développeurs à effectuer plusieurs recherches plutôt qu'une seule.
Voici une liste d'exemples :
Utiliser ces fonctions | Ne pas utiliser |
---|---|
DateSys() | DateDuJour() |
HeureSys() | Maintenant() |
DateVersJourDeLaSemaine() | DateVersJour() |
API() | Appel32() |
FenPrécédente() | ParentObjet(MaFenêtre) |
FenRepeint() | MultitâcheRepeint() |
Ferme(<NomFenêtre>) | FenEtat(<NomFenêtre>, Inexistant) |
DonneFocusEtRetourUtilisateur() | RepriseSaisie() |
gValeurMémorisée() | gLien() |
ChaîneVersNumérique() | Val() |
fRepExiste() | fRépertoireExiste() |
fRepTemp() | fRépertoireTemp() |
PROCÉDURE | FONCTION |
... | ... |
S'il y a moins de code alors il y aura moins de bugs. Le code source sera moins long à comprendre et plus facile à lire et à maintenir.
C'est un fait reconnu que le code est lu beaucoup plus souvent qu'il n'est écrit. C'est pourquoi sa lisibilité est essentielle.
Cependant réduire et optimiser la taille du code ne signifie pas nécessairement que le code sera plus rapide.
A titre d'exemple, le premier code ci-dessous peut être réduit par le deuxième :
bEstFichierTexte est un booléen
SI (fFichierExiste(sUnFichier) = Vrai) ALORS
SI (fExtraitChemin(sUnFichier, fExtension) = ".txt") ALORS
bEstFichierTexte = Vrai
SINON
bEstFichierTexte = Faux
FIN
SINON
bEstFichierTexte = Faux
FIN
ST_Rectangle est une Structure
nX est un entier
nY est un entier
nLargeur est un entier
nHauteur est un entier
FIN
stUnRectangle est un ST_Rectangle
stUnRectangle.nX = 0
stUnRectangle.nY = 0
stUnRectangle.nLargeur = 0
stUnRectangle.nHauteur = 0
ST_Point est une Structure
nX est un entier
nY est un entier
FIN
stUnPoint est un ST_Point
stUnPoint.nX = 0
stUnPoint.nY = 0
tabEntier est un tableau de entier = [1, 2, 3, 4, 5]
nSomme est un entier
POUR i = 1 _À_ tabEntier..Occurrence
nSomme += tabEntier[i]
FIN
bEstFichierTexte est un booléen
bEstFichierTexte = (fFichierExiste(sUnFichier) _ET_ (fExtraitChemin(sUnFichier, fExtension) = ".txt"))
UnRectangle est un Rectangle
VariableRAZ(UnRectangle)
UnPoint est un Point
VariableRAZ(UnPoint)
tabEntier est un tableau de entier = [1, 2, 3, 4, 5]
nSomme est un entier = Somme(tabEntier)
Dans le code ci-dessous la trace affichera "30/04/2019" au lieu de "31/04/2019" souhaité.
dTemp est une Date = "20120923"
dTemp..Jour = 31 // Cette ligne est à déplacer avant la fonction Trace()
dTemp..Mois = 4
Trace(DateVersChaîne(dTemp, "JJ/MM/AAAA"))
L'exécutation du code ci-dessous est correcte, c'est-à-dire il donne le bon résultat quelque soit la réponse de l'utilisateur :
Cependant la documentation précise que le résultat de la fonction OuiNon() est une des deux constantes Oui ou Non.SI (OuiNon("Etes-vous un humain ?") = Vrai) ALORS
Info("L'utilisateur a dit Oui.")
SINON
Info("L'utilisateur a dit Non.")
FIN
Pour revenir dans un état cohérent, remplacer Inexistant par threadInexistant.nChiffreDeux est un entier
PROCÉDURE INTERNE piInit()
ThreadPause(100)
nChiffreDeux = 2
FIN
ThreadExécute("piInit", threadNormal, piInit)
TANTQUE (ThreadEtat("piInit") <> Inexistant)
Temporisation(25, tempoSourisEtClavier)
FIN
Trace( nChiffreDeux )
Nous allons créer un algorithme simple qui calcule la somme de deux nombres.
Cette procédure attend deux paramètres (nombre1 et nombre2) et renvoie la somme des 2 paramètres grâce à l'opérateur +.PROCÉDURE AdditionnerDeuxNombres(nombre1, nombre2)
RENVOYER nombre1 + nombre2
Le résultat '3' est bien celui attendu. C'est-à-dire que la variable nommée 'laSomme' vaut 3.soit laSomme = AdditionnerDeuxNombres(1, 2)
Info("La somme de 1+2 vaut : "+laSomme)
// La somme de 1+2 vaut : 3
Le résultat est "ab" car l'opérateur + permet aussi la concaténation de 2 chaînes.soit laSomme = AdditionnerDeuxNombres("a", "b")
Info("La somme de 'a'+'b' vaut : "+laSomme)
// La somme de 'a'+'b' vaut : ab
Nous venons d'ajouter un type aux paramètres de la procédure. Grâce à cela, la procédure nommée "AdditionnerDeuxNombres" attend réellement 2 numériques.PROCÉDURE AdditionnerDeuxNombres(nombre1 est un numérique, nombre2 est un numérique)
RENVOYER nombre1 + nombre2
Le programme essaie de convertir les caractères "a" et "b" en numérique. Il n'y arrive pas donc dans la procédure, les variables nombre1 et nombre2 vaut 0 (zéro), d'où le résultat final 0 (zéro).soit laSomme = AdditionnerDeuxNombres("a", "b")
Info("La somme de 'a'+'b' vaut : "+laSomme)
// La somme de 'a'+'b' vaut : 0
Nous pouvons aller plus loin en précisant le type de retour de la procédure.soit laSomme = AdditionnerDeuxNombres("1", "2")
Info("La somme de '1'+'2' vaut : "+laSomme)
// La somme de '1'+'2' vaut : 3
Rajouter le type de retour d'une procédure est une bonne pratique. Si le code source de la procédure est longue de plusieurs lignes alors un développeur pourra facilement savoir le type de retour de la procédure en observant simplement sa déclaration et sa signature :PROCÉDURE AdditionnerDeuxNombres(nombre1 est un numérique, nombre2 est un numérique) : numérique
RENVOYER nombre1 + nombre2
PROCÉDURE AdditionnerDeuxNombres(nombre1 est un numérique, nombre2 est un numérique) : numérique
D'un seul coup d'oeil, le développeur sait que la procédure additionne 2 nombres grâce au nom de la procédure. De plus, elle attend 2 numériques en paramètre (nombre1 et nombre2) et elle retourne un numérique. Ainsi nous pouvons également mieux utiliser cette procédure en ajoutant le type de la variable (numérique) retourné :// Résumé : Calcule la somme de 2 nombres.
// Paramètres :
// xNombre1 (numérique) : le 1er nombre
// xNombre2 (numérique) : le 2ème nombre
// Valeur de retour :
// numérique : somme des 2 paramètres (1er nombre + 2ème nombre)
//
PROCÉDURE AdditionnerDeuxNombres(xNombre1 est un numérique, xNombre2 est un numérique) : numérique
RENVOYER (xNombre1 + xNombre2)
De plus comme nous avons utilisé le type de variable "Numérique", les nombres décimaux avec virgule sont gérés :xLaSomme est un numérique = AdditionnerDeuxNombres(1, 2)
Info("La somme de '1'+'2' vaut : "+xLaSomme)
// La somme de '1'+'2' vaut : 3
Si nous avions utilisé le type de variable "Entier" au niveau des paramètres de cette fonction alors les nombres décimaux avec virgule n'auraient pas été gérés. Et la fonction auraient renvoyée une valeur incohérente.xLaSomme est un numérique = AdditionnerDeuxNombres(1.2, 2.3)
Info("La somme de '1.2'+'2.3' vaut : "+xLaSomme)
// La somme de '1.2'+'2.3' vaut : 3.5
PROCÉDURE AdditionnerDeuxEntiers(nNombre1 est un entier, nNombre2 est un entier) : entier
RENVOYER (nNombre1 + nNombre2)
Ajouter le mot-clé LOCALE car les paramètres xNombre1 et xNombre2 ne sont pas modifiés dans la procédure et ne doivent pas l’être.nLaSomme est un numérique = AdditionnerDeuxEntiers(1.4, 2.3)
Info("La somme de '1.4'+'2.3' vaut : "+nLaSomme)
// La somme de '1.4'+'2.3' vaut : 3 - la valeur est incohérente : 1.4+2.3 <> 3
PROCÉDURE AdditionnerDeuxNombres(LOCALE xNombre1 est un numérique, LOCALE xNombre2 est un numérique) : numérique
RENVOYER (xNombre1 + xNombre2)
// Tester avec des nombres :
// Test avec résultat positif et l'élément neutre (zéro)
dbgVerifieEgalite( AdditionnerDeuxNombres(1, 2), 3)
dbgVerifieEgalite( AdditionnerDeuxNombres(2, 1), 3) // Commutativité de l'addition
dbgVerifieEgalite( AdditionnerDeuxNombres(4, 0), 4)
dbgVerifieEgalite( AdditionnerDeuxNombres(0, 4), 4) // Commutativité de l'addition
// Test avec résultat négatif
dbgVerifieEgalite( AdditionnerDeuxNombres(-1, -2), -3)
dbgVerifieEgalite( AdditionnerDeuxNombres(-2, -1), -3) // Commutativité de l'addition
// Test avec résultat décimal
dbgVerifieEgalite( AdditionnerDeuxNombres(1.2, 2.3), 3.5)
dbgVerifieEgalite( AdditionnerDeuxNombres(2.3, 1.2), 3.5) // Commutativité de l'addition
dbgVerifieEgalite( AdditionnerDeuxNombres(-1.2, -2.3), -3.5)
dbgVerifieEgalite( AdditionnerDeuxNombres(-2.3, -1.2), -3.5) // Commutativité de l'addition
// Test avec résultat à 0 (zéro)
dbgVerifieEgalite( AdditionnerDeuxNombres(1, -1), 0)
dbgVerifieEgalite( AdditionnerDeuxNombres(-1, 1), 0) // Commutativité de l'addition
dbgVerifieEgalite( AdditionnerDeuxNombres(0, 0), 0)
// Tester avec des chaînes :
// Test avec résultat positif et l'élément neutre (zéro)
dbgVerifieEgalite( AdditionnerDeuxNombres("1", "2"), 3)
dbgVerifieEgalite( AdditionnerDeuxNombres("2", "1"), 3) // Commutativité de l'addition
dbgVerifieEgalite( AdditionnerDeuxNombres("4", "0"), 4)
dbgVerifieEgalite( AdditionnerDeuxNombres("0", "4"), 4) // Commutativité de l'addition
// Test avec résultat négatif
dbgVerifieEgalite( AdditionnerDeuxNombres("-1", "-2"), -3)
dbgVerifieEgalite( AdditionnerDeuxNombres("-2", "-1"), -3) // Commutativité de l'addition
// Test avec résultat décimal
dbgVerifieEgalite( AdditionnerDeuxNombres("1.2", "2.3"), 3.5)
dbgVerifieEgalite( AdditionnerDeuxNombres("2.3", "1.2"), 3.5) // Commutativité de l'addition
dbgVerifieEgalite( AdditionnerDeuxNombres("-1.2", "-2.3"), -3.5)
dbgVerifieEgalite( AdditionnerDeuxNombres("-2.3", "-1.2"), -3.5) // Commutativité de l'addition
// Test avec résultat à 0 (zéro)
dbgVerifieEgalite( AdditionnerDeuxNombres("1", "-1"), 0)
dbgVerifieEgalite( AdditionnerDeuxNombres("-1", "1"), 0) // Commutativité de l'addition
dbgVerifieEgalite( AdditionnerDeuxNombres("0", "0"), 0)
// Test avec caractère et chaîne
dbgVerifieEgalite( AdditionnerDeuxNombres("a", "b"), 0)
dbgVerifieEgalite( AdditionnerDeuxNombres("nombre", "chiffre"), 0)
// A FAIRE : Tests aux extrémités (positif et négatif) : ajouter "est un numérique (*)"
Il faut garder à l'esprit que coder en langage WL5® ne signifie pas que le développeur ne doit pas prendre en compte les problématiques mémoire lorsqu'il programme. En effet, une vraie prise en compte est nécessaire même si le raisonnement est différent de ce qu'il peut-être dans d'autres langages.
Les outils de PCSOFT® vous permettent donc de pratiquement tout faire. Pour certains, il est tentant de considérer qu'ils sont suffisants et qu'il ne faille pas aller voir ailleurs de temps à autre. C'est une grave erreur ! En effet, bien que ces outils permettent de tout faire, cela ne signifie pas qu'ils soient la meilleure solution dans tous les cas. Ainsi chaque langage de programmation présente des avantages et des inconvénients en fonction du type d'application à implémenter et du domaine cible.
Avec moins de lignes de code, les développeurs ont plus de temps à consacrer au besoin même du métier. Le développeur peut se concentrer sur les besoins utilisateurs à forte valeur ajoutée.
L'une des forces du WLangage® est sa simplicité. Cependant comme n'importe quel langage de programmation, WLangage® réserve aussi sa part de subtilités que l'on saisit avec l'expérience.
Un développeur a 2 choses en main : le problème qu'on lui demande de résoudre et la technique qui lui permet de le résoudre.
On cherche à avoir la technique la plus efficace afin de se concentrer sur le problème.
L'objectif : c'est le problème. La technique n'est qu'un moyen.
Donc il existe des outils pour simplifier la vie des développeurs, pour rendre la partie technique plus efficace. C'est tout naturel.
Seulement ces outils censés simplifier la vie ajoutent en réalité une "charge mentale" aux développeurs.
Cette charge mentale est la connaissance/maitrise de ces outils tels que GIT, Docker, les différents frameworks, ...