Pas terrible ; mais presque
Cet article est librement inspiré et traduit d’un article de net.tutsplus.com.
Quel que soit notre niveau de maîtrise, nous avons tous été débutants. Commettant des erreurs de débutants. Cet article ne fera pas de vous un expert infaillible (si cela existe quelque part). Il fait juste un petit tour d’horizon des erreurs à éviter ou des mauvaises habitudes qu’il est déconseillé de prendre lorsqu’on débute.
Le DOM est lent. Il est donc conseillé de limiter sa manipulation pour limiter l’impact sur les performances. Un exemple de (mauvais) code:
for (var i = 0; i < 100; i++){ var li = $("<li>").html("Ceci est la liste numéro #" + (i+1)); $("#une_liste_UL").append(li); }
Ce code modifie 100 fois le DOM, et créer au passage 100 objets jQuery ! Voyons comment produire un code beaucoup plus économe. Une bonne méthode consisterait à créer une chaîne de caractères contenant les 100 <li> et de les ajouter en une fois au HTML. Une fois au lieu de 100…
var laliste = ""; for (var i = 100; i > 0; i--){ laliste += "<li>Ceci est la liste numéro #" + (99- i); } document.getElementById("une_liste_UL").innerHTML(laliste);
Encore une fois, faire le boulot en une fois au lieu de 100 est certainement un gain de temps, d’autant plus, qu’ici, nous faisons appel à la concaténation de chaîne sans passer par des objets intermédiaires. Une autre solution utilisant les tableaux pourrait ressembler à ceci :
var laliste = "<li>" var lis = []; for (var i = 100; i > 0; i--){ lis.push("Ceci est la liste numéro #" + (99- i)); } laliste += lis.join("</li><li>") + "</li>"; document.getElementById("une_liste_UL").innerHTML(laliste);
Dans le cas de concaténation de grande chaîne de caractère, découper celle-ci en tableau permet d’en optimiser un peu plus l’assemblage. Ceci est la méthode la plus rapide pour construire une structure répétitive en JavaScript sans utiliser une librairie de template ou un framework.
Ce qui suit ne règle pas un problème de performance ; mais est extrêmement important quand vous travaillez avec d’autres personnes sur le même code (et même pour soit !) : gardez la cohérence des identifiants (variables, noms de fonctions). Un exemple :
var foo = "bar"; var plante = "verte"; var voiture = "rouge";
Ajouter une variable qui s’appellerait « Quelquechose » serait introduire une certaine confusion sur plusieurs plans : outre le fait qu’elle ne désigne pas grand-chose mais elle utilise aussi une lettre majuscule alors que les autres variables n’en n’utilisent pas. Ce qui est courant par contre, et ce, dans de nombreux langages, c’est de nommer les constantes en majuscule pour bien les distinguer des « variables ».
On peut aller un peu plus loin dans ce sens en utilisant des noms de fonction dont la longueur est proche et dont la syntaxe est constante :
function soustraitCinq(nombre){ return nombre - 5; }
Et
function ajouteCinq(nombre){ return nombre + 5; }
Les deux fonctions utilisent la même syntaxe (nom de l’action en toutes lettres, et non pas soustraitCinq et plusCinq) ; par ailleurs, le nom de la fonction peut aussi indiquer ce qu’elle retourne, par exemple, la fonction getTweetHTML retournera le résultat de ses investigations sous forme de code HTML. Vous pouvez aussi préfixer le nom de la fonction par « fait » ou « do » si la fonction se contente de faire quelque chose sans rien retourner.
La règle générale est de nommer en tenant compte d’une syntaxe forte et descriptive (autant que faire se peut).
Les tableaux en JavaScript ne sont pas associatifs, tenter de les utiliser comme tel serait donc une erreur. Par contre, les objets eux peuvent être traités comme des tables de hachage, et on peut en parcourir les propriétés à l’aide d’une boucle for…in comme ici :
for (var prop in unObjet) { alert(unObjet[prop]); }
Le problème c’est que le parcourt en question se fait sur tous les objets même si parmi eux certains ne possèdent pas la propriété attendue. Mais heureusement, ceci se comble facilement avec la méthode hasOwnProperty() :
for (var prop in unObjet) { if (unObjet.hasOwnProperty(prop)) { alert(unObjet[prop]); } }
Comparer les valeurs booléennes dans une condition est une perte de temps, comme dans l’exemple ci-dessous :
if (foo == true) { // fait ceci si vrai } else { // fait cela si faux }
Il est inutile de demander à foo s’il est vrai ou faux dans la mesure où cette variable est une valeur booléenne (vraie ou fausse), la question se pose alors de manière plus élégante :
if (foo) { // fait ceci si vrai } else { // fait cela si faux }
Si on veu tester l’état faux, il suffit d’utiliser l’opérateur NOT comme ici :
if (!foo) { // fait ceci si vrai } else { // fait cela si faux }
Les événements sont un sujet épineux en JavaScript. Ils sont loin les jours où on plaçait les événements directement dans le code HTML (les onclick et consorts).En lieu et place, utilisez la propagation et la délégation.
Imaginons que vous ayez une grille d’images qui s’affichent dans une lightbox modale.
Le code HTML
<div id="lagrille"> <a href="image.jpg"><img src="image_vignette.jpg"></a> <a href="image.jpg"><img src="image_vignette.jpg"></a> <a href="image.jpg"><img src="image_vignette.jpg"></a> ... </div>
Le (mauvais) JavaScript :
$('a').on('click', function() { lightBox(this); });
Ce code se contente de passer à la lightBox l’adresse de l’image haute résolution, au lieu de lier effectivement les liens contenus dans la grille, il est donc préférable de faire comme suit :
$("#lagrille").on("click", "a", function(event) { lightBox(event.target); });
De la sorte,this et event.target font référence à l’ancre. Vous pouvez utiliser cette technique pour n’importe quel éléments parent, il faut juste s’assurer que bien lier le bon événement à la bonne cible.
Que cela soit en JavaScript ou en PHP, l’opérateur ternaire est parfois utilisé de manière redondante.
// javascript return foo.toString() !== "" ? true : false; // php return (laFonction()) ? true : false;
Une condition renvoyant toujours un booléen, il n’est pas nécessaire d’ajouter true/false, il suffit juste de retourner la condition !
// javascript return foo.toString() !== ""; // php return laFonction();
Le test if…else est central dans la plupart des langages.
Dans le cas ou la condition statue sur le remplissage d’une variable, l’opérateur ternaire peut être une solution élégante :
if ($formule) { $post->message = 'Bonjour'; } else { $post->message = 'Au revoir'; }
Ce code peut être élégamment remplacé par une seule ligne :
$post->message = $formule ? 'Bonjour' : 'Au revoir';
Cependant, aussi clair qu’il soit, le plus important de ne pas le sur-utiliser. Gardez en tête que la clarté ne repose pas toujours sur la concision.
Construire et surtout maintenir un code avec de nombreux tests imbriqués n’est pas une sinécure. Voici un exemple (loin d’être anecdotique) :
$message-derreur = null; if ($this->form_validation->run()) { if ($this->upload->do_upload()) { $image = $this->upload->get_info(); if ( ! $this->image->create_thumbnail($image['nom_fichier'], 300, 150)) { $message-derreur = 'Erreur à la création de la vignette.'; } } else { $message-derreur = 'Erreur durant l\'upload de l\'image.'; } } else { $message-derreur = $this->form_validation->error_string(); } // Show error messages if ($message-derreur !== null) { $this->load->view('form', array( 'error' => $message-derreur, )); } // Sauvegarde de la page else { $qq-donnees['image'] = $image['nom_fichier']; $this->mon_model->sauve($qq-donnees); }
Vous aussi vous avez mal au crâne, c’est normal. Heureusement, il y a une manière de faire plus simple comme ici :
try { if ( ! $this->form_validation->run()) { throw new Exception($this->form_validation->error_string()); } if ( ! $this->upload->do_upload()) { throw new Exception('Erreur durant l\'upload de l\'image.'); } $image = $this->upload->get_info(); if ( ! $this->image->create_thumbnail($image['nom_fichier'], 300, 150)) { throw new Exception('Erreur à la création de la vignette.'); } } // affiche les messages d'erreur catch (Exception $e) { $this->load->view('form', array( 'error' => $e->getMessage(), )); // utiliser une méthode de sortie ou exit return; } // si vous êtes arrivés ici, c'est que tout c'est bien déroulé. $qq-donnees['image'] = $image['nom_fichier']; $this->mon_model->sauve($qq-donnees);
Cela peut conduire à un même nombre de lignes ; mais la logique est beaucoup plus claire et le code plus prompte à l’entretien. Sans compter que le débogage est plus simple, on l’on sait tout de suite où placer les if qui vont bien pour prendre en compte la nouvelle situation.
Les développeurs Ruby et Python sont connus pour traquer la plus petite exception. Bien que fastidieuse, cette méthodologie est une bonne chose, car si quelque chose tourne mal, une exception est aussitôt lancée et permet de localiser rapidement d’où vient le problème.
En PHP, et plus particulièrement avec les frameworks « anciens » comme CakePHP ou CodeIgniter pratiquent ce que l’on peut appelé du code « faussement-content ». Au lieu de lancer une exception, ils préfèrent renvoyer un false et assigner un message d’erreur dans une propriété, ce qui force le programmeur à aller pêcher l’erreur à l’aide de la méthode get_error().
Utiliser la méthode de « l’heureuse exception » est bien plus productif. Si le framework que vous utilisez possède une classe de gestion des exceptions, il est bien plus simple de l’étendre avec vos propres exceptions et ainsi simplifier considérablement le débogage.
class MonException extends Exception {}
Il est d’usage d’utiliser les instructions conditionnelles pour faire modifier le flux d’exécution. Il est tentant de tester une condition puis d’exécuter beaucoup de codes si la condition est vraie et de reporter dans le else tout ce qui ne remplit pas la condition.
Ce type de solution, présente un risque de « spaghettisation » du code. On peut rendre ce code beaucoup plus lisible si nous renversons la condition comme suit :
function uneFonction($param) { if ($param != 'OK') return false; $this->faitUntruc(); return true; }
Cette technique combinée aux deux autres vues précédemment permet d’avoir un code à la fois beaucoup plus élégant mais aussi bien plus facile à entretenir.
La boucle for est utilisée couramment quand vous avez besoin d’un compteur comme ici :
for (var i = 0; i < x; i++) { ... }
Il y a de très bonnes raisons d’utiliser la boucle for
; mais pour des cas aussi simples, la boucle while
est bien plus efficace :
var i = x; while (i--) { ... }
Et traditionnellement la boucle while
est un peu plus rapide que la boucle for
, il est donc préférable de l’utiliser dès que c’est possible.
Une méthode remplit en général une fonction simple et limiter à une taille raisonnable les méthodes est un gage de bon fonctionnement et accessoirement d’évolutivité. Voici par exemple un petit monstre :
class SomeClass { function monsterMethod() { if($weArePilots) { $this->goAndDressUp(); $this->washYourTeeth(); $this->cleanYourWeapon(); $this->takeYourHelmet(); if($this->helmetDoesNotFit()) $this->takeAHat(); else $this->installHelmet(); $this->chekcYourKnife(); if($this->myAirplain() == "F22") $this->goToArmyAirport(); else $this->goToCivilianAirport(); $this->aim(); $this->prepare(); $this->fire(); } } }
En découpant ce monstre en unités fonctionnelles plus petites, on s’assure un entretien plus facile (et une plus grande universalité du code). C’est une des erreurs les plus communes mises en œuvre par les débutants.
class SomeClass { function monsterMethod() { if($weArePilots) { $this->prepareYourself(); $this->tryHelmet(); $this->findYourAirport(); $this->fightEnemy(); } } private function prepareYourself() { $this->goAndDressUp(); $this->washYourTeeth(); $this->cleanYourWeapon(); $this->chekcYourKnife(); } private function tryHelmet() { $this->takeYourHelmet(); if($this->helmetDoesNotFit()) $this->takeAHat(); else $this->installHelmet(); } private function findYourAirport() { if($this->myAirplain() == "F22") $this->goToArmyAirport(); else $this->goToCivilianAirport(); } private function fightEnemy() { $this->aim(); $this->prepare(); $this->fire(); } }
Un trop grand nombre d’imbrications dans le code le rendent difficile à entretenir, exemple :
function doSomething() { if ($someCondition) { if ($someOtherCondition) { if ($yetSomeOtherCondition) { doSomethingSpecial(); } doSomethingElse(); } } }
En utilisant le conseil #10 on obtient quelque chose de plus convivial :
function doSomething() { if (!$someCondition) { return false; } if (!$someOtherCondition) { return false; } if ($yetSomeOtherCondition) { doSomethingSpecial(); } doSomethingElse(); }
Si vous êtes confronté à ce type d’imbrication, il y a de fortes chances que votre code puisse être décomposé en unité fonctionnelle plus simple :
function someFunc() { if($oneThing) { $this->doSomething(); if($anotherThing) $this->doSomethingElse(); } }
Cette méthode est décomposable en deux sous-méthode plus claires :
function someFunc() { if($oneThing) { $this->doSomething(); $this->doAnotherThing($anotherThing); } } private doAnotherThing($anotherThing) { if($anotherThing) $this->doSomethingElse(); }
Lorsque vous êtes confronté vous-même à une trop grande imbrication des if, examinez votre code, il se pourrait que votre méthode veuille faire trop de choses, et vous aurez tout intérêt à segmenter ses actions et donc à créer d’autres méthodes. Un exemple :
function someFunct() { $this->order->set(23); $this->order->addProduct('superComputer'); $this->shoppingList->add('superComputer'); }
Dans ce cas, il faut extraire la méthode imbriquée et pour lui donner une existence propre.
function someFunct() { $orderId = 23; $selectedProductName = 'superComputer'; $this->order->set($orderId); $this->order->addProduct($selectedProductName); $this->shoppingList->add($selectedProductName); }
Chiffres et chaînes magiques sont souvent utilisés comme paramètres lors d’un appel de fonction ou de méthode sans autre forme d’explication et qui rendent si difficile la compréhension du schmilblick pas seulement pour les autres, mais aussi pour vous passé quelque temps. Au lieu de ceci :
function someFunct() { $this->order->set(23); $this->order->addProduct('superComputer'); $this->shoppingList->add('superComputer'); }
Spécifiez ce que ces chiffres et chaîne veulent dire, pour ce faire, il suffit de les assignés à une variable au nom explicite !
function someFunct() { $orderId = 23; $selectedProductName = 'superComputer'; $this->order->set($orderId); $this->order->addProduct($selectedProductName); $this->shoppingList->add($selectedProductName); }
Certains sacrifient la création de variables sur l’hôtel de la performance ; cependant, retenez qu’il faut toujours donner la priorité à la lisibilité. L’optimisation devient nécessaire quand vous pouvez dire pourquoi elle est nécessaire.
Plutôt que d’utiliser des foreach() à répétition pour faire des choses élémentaires comme :
foreach (&$myArray as $key =>$element) { if ($element > 5) unset ($myArray[$key]); }
Il est préférable de faire comme ceci :
$myArray = array_filter($myArray, function ($element) { return $element <= 5;});
PHP offre une large variété de méthodes dédiées aux tableaux. Certes, pas toujours simple de prime abord, mais prenez le temps de les apprendre, vous y gagnerez à la longue.
Malgré ce qui a été dit au point n°14, il faut cependant faire attention de ne pas générer des variables inutiles. Regardez ce code :
public function get_posts() { $query = $this->db->get('posts'); $result = $query->result(); return $result; }
La variable $result est ici totalement inutile, le code suivant est tout aussi efficace et clair :
public function get_posts() { $query = $this->db->get('posts'); return $query->result(); }
Une base de données est conçue pour manipuler des informations, utilisez les outils qu’elle met à votre disposition pour concevoir des applications plus efficaces. Par exemple, vous pouvez réduire le nombre de requêtes redondantes dans pas mal de circonstances. La plupart des scripts de gestion utilisateur font au moins deux requêtes pour un enregistrement, une pour vérifier si l’email ou le nom d’utilisateur n’existe pas puis une autre requête pour l’enregistrement lui-même. Il serait préférable de faire en sorte que le champ soit UNIQUE et alors utiliser les fonctions natives de MySQL pour la vérification puis l’insertion (ou pas).
Moins on code mieux on code.
Le temps où l’on nommait les variables x
, y
ou z
est révolu (à moins que cela ne soit des coordonnées spatiales ). Les variables représentent une part importante de la logique de votre développement. Si vous ne voulez pas taper des noms longs pour vos variables, changez de crémerie. Heu, changez d’IDE ! Les plus modernes d’entre eux proposent une complétion qui réagit au quart de tour même pour les variables.
Codez toujours comme si vous le faisiez dans l’optique de revenir sur le code six mois plus tard. Êtes-vous certain que la variable
$truc
aura encore une signification pour vous dans un an ? Certainement pas, donc, soyez descriptif.
Nommez vos méthodes avec des verbes représentant l’action clé. Le concept est exactement l’inverse que pour les variables. Utilisez une forme courte, mais descriptive. Privilégiez les méthodes au nom court pour les méthodes publiques et des noms plus long (et donc plus descriptifs) pour les méthodes privées. Ceci devrait aider à lire votre code comme une belle prose. Évitez d’utiliser une autre langue que l’anglais pour le code. Pourquoi ? Si vous développez des plug-in pour WordPress ou jQuery, une quasi-majorité de vos utilisateurs seront anglophones, chouchoutez-lès. L’anglais est clair et concis, c’est la langue internationale de la programmation, c’est un fait, tenez-en compte ; après si vous voulez jouer l’exception culturelle, c’est votre choix.
Finalement, la structure du code est une chose aussi importante que la lisibilité et que toutes les choses que nous venons de voir. Voici donc deux recommandations :
“Je n’ai jamais commis d’erreur stupide” Personne. Jamais.
Répondre
Vous devez être connecté pour poster un commentaire.