Pas terrible ; mais presque

Cet article est libre­ment ins­pi­ré et tra­duit d’un article de net.tutsplus.com.

Quel que soit notre niveau de maî­trise, nous avons tous été débu­tants. Com­met­tant des erreurs de débu­tants. Cet article ne fera pas de vous un expert infaillible (si cela existe quelque part). Il fait juste un petit tour d’ho­ri­zon des erreurs à évi­ter ou des mau­vaises habi­tudes qu’il est décon­seillé de prendre lors­qu’on débute.

JAVASCRIPT

1/ manipulation inutile du DOM

Le DOM est lent. Il est donc conseillé de limi­ter sa mani­pu­la­tion pour limi­ter l’im­pact sur les per­for­mances. Un exemple de (mau­vais) 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 modi­fie 100 fois le DOM, et créer au pas­sage 100 objets jQue­ry ! Voyons com­ment pro­duire un code beau­coup plus éco­nome. Une bonne méthode consis­te­rait à créer une chaîne de carac­tères conte­nant les 100 <li> et de les ajou­ter 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 bou­lot en une fois au lieu de 100 est cer­tai­ne­ment un gain de temps, d’au­tant plus, qu’i­ci, nous fai­sons appel à la conca­té­na­tion de chaîne sans pas­ser par des objets inter­mé­diaires. Une autre solu­tion uti­li­sant les tableaux pour­rait res­sem­bler à 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 conca­té­na­tion de grande chaîne de carac­tère, décou­per celle-ci en tableau per­met d’en opti­mi­ser un peu plus l’as­sem­blage. Ceci est la méthode la plus rapide pour construire une struc­ture répé­ti­tive en JavaS­cript sans uti­li­ser une librai­rie de tem­plate ou un framework.

2/ inconsistance des noms de variables et de fonction en JavaScript

Ce qui suit ne règle pas un pro­blème de per­for­mance ; mais est extrê­me­ment impor­tant quand vous tra­vaillez avec d’autres per­sonnes sur le même code (et même pour soit !) : gar­dez la cohé­rence des iden­ti­fiants (variables, noms de fonc­tions). Un exemple :

var foo = "bar";
var plante = "verte";
var voiture = "rouge";

Ajou­ter une variable qui s’ap­pel­le­rait « Quel­que­chose » serait intro­duire une cer­taine confu­sion sur plu­sieurs plans : outre le fait qu’elle ne désigne pas grand-chose mais elle uti­lise aus­si une lettre majus­cule alors que les autres variables n’en n’u­ti­lisent pas. Ce qui est cou­rant par contre, et ce, dans de nom­breux lan­gages, c’est de nom­mer les constantes en majus­cule pour bien les dis­tin­guer des « variables ».
On peut aller un peu plus loin dans ce sens en uti­li­sant des noms de fonc­tion dont la lon­gueur est proche et dont la syn­taxe est constante :

function soustraitCinq(nombre){
   return nombre - 5;
}

Et

function ajouteCinq(nombre){
  return nombre + 5;
}

Les deux fonc­tions uti­lisent la même syn­taxe (nom de l’ac­tion en toutes lettres, et non pas sous­trait­Cinq et plus­Cinq) ; par ailleurs, le nom de la fonc­tion peut aus­si indi­quer ce qu’elle retourne, par exemple, la fonc­tion getT­wee­tHTML retour­ne­ra le résul­tat de ses inves­ti­ga­tions sous forme de code HTML. Vous pou­vez aus­si pré­fixer le nom de la fonc­tion par « fait » ou « do » si la fonc­tion se contente de faire quelque chose sans rien retourner.

La règle géné­rale est de nom­mer en tenant compte d’une syn­taxe forte et des­crip­tive (autant que faire se peut).

3/ Utiliser hasOwnProperty() dans les boucles for…in

Les tableaux en JavaS­cript ne sont pas asso­cia­tifs, ten­ter de les uti­li­ser comme tel serait donc une erreur. Par contre, les objets eux peuvent être trai­tés comme des tables de hachage, et on peut en par­cou­rir les pro­prié­tés à l’aide d’une boucle for…in comme ici :

for (var prop in unObjet) {
  alert(unObjet[prop]);
}

Le pro­blème c’est que le par­court en ques­tion se fait sur tous les objets même si par­mi eux cer­tains ne pos­sèdent pas la pro­prié­té atten­due. Mais heu­reu­se­ment, ceci se comble faci­le­ment avec la méthode hasOwn­Pro­per­ty() :

for (var prop in unObjet) {
  if (unObjet.hasOwnProperty(prop)) {
    alert(unObjet[prop]);
  }
}

4/ Comparer les valeurs booléennes

Com­pa­rer les valeurs boo­léennes dans une condi­tion 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 deman­der à foo s’il est vrai ou faux dans la mesure où cette variable est une valeur boo­léenne (vraie ou fausse), la ques­tion se pose alors de manière plus élégante :

if (foo) {
  // fait ceci si vrai
} else {
  // fait cela si faux
}

Si on veu tes­ter l’é­tat faux, il suf­fit d’u­ti­li­ser l’o­pé­ra­teur NOT comme ici :

if (!foo) {
  // fait ceci si vrai
} else {
 // fait cela si faux
}

5/ Bien affecter les événements

Les évé­ne­ments sont un sujet épi­neux en JavaS­cript. Ils sont loin les jours où on pla­çait les évé­ne­ments direc­te­ment dans le code HTML (les onclick et consorts).En lieu et place, uti­li­sez la pro­pa­ga­tion et la délégation.
Ima­gi­nons que vous ayez une grille d’i­mages qui s’af­fichent dans une light­box 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 (mau­vais) JavaScript :

$('a').on('click', function() {
  lightBox(this);
});

Ce code se contente de pas­ser à la light­Box l’a­dresse de l’i­mage haute réso­lu­tion, au lieu de lier effec­ti­ve­ment les liens conte­nus 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 pou­vez uti­li­ser cette tech­nique pour n’im­porte quel élé­ments parent, il faut juste s’as­su­rer que bien lier le bon évé­ne­ment à la bonne cible.

6/ Éviter la redondance de l’opérateur ternaire

Que cela soit en JavaS­cript ou en PHP, l’o­pé­ra­teur ter­naire est par­fois uti­li­sé de manière redondante.

// javascript
return foo.toString() !== "" ? true : false;

// php
return (laFonction()) ? true : false;

Une condi­tion ren­voyant tou­jours un boo­léen, il n’est pas néces­saire d’a­jou­ter true/false, il suf­fit juste de retour­ner la condition !

// javascript
return foo.toString() !== "";

// php
return laFonction();

PHP

7/ Utilisez l’opérateur ternaire quand c’est nécessaire

Le test if…else est cen­tral dans la plu­part des langages.
Dans le cas ou la condi­tion sta­tue sur le rem­plis­sage d’une variable, l’o­pé­ra­teur ter­naire peut être une solu­tion élégante :

if ($formule)
{
  $post->message = 'Bonjour';
}
else
{
  $post->message = 'Au revoir';
}

Ce code peut être élé­gam­ment rem­pla­cé par une seule ligne :

$post->message = $formule ? 'Bonjour' : 'Au revoir';

Cepen­dant, aus­si clair qu’il soit, le plus impor­tant de ne pas le sur-utiliser. Gar­dez en tête que la clar­té ne repose pas tou­jours sur la concision.

8/ Rejetez dès le début les exceptions dans le cas de test en cascade

Construire et sur­tout main­te­nir un code avec de nom­breux tests imbri­qués n’est pas une siné­cure. Voi­ci 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 aus­si vous avez mal au crâne, c’est nor­mal. Heu­reu­se­ment, 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 beau­coup plus claire et le code plus prompte à l’en­tre­tien. Sans comp­ter que le débo­gage est plus simple, on l’on sait tout de suite où pla­cer les if qui vont bien pour prendre en compte la nou­velle situation.

9/ La méthode faux-heureux

Les déve­lop­peurs Ruby et Python sont connus pour tra­quer la plus petite excep­tion. Bien que fas­ti­dieuse, cette métho­do­lo­gie est une bonne chose, car si quelque chose tourne mal, une excep­tion est aus­si­tôt lan­cée et per­met de loca­li­ser rapi­de­ment d’où vient le problème.
En PHP, et plus par­ti­cu­liè­re­ment avec les fra­me­works « anciens » comme Cake­PHP ou CodeI­gni­ter pra­tiquent ce que l’on peut appe­lé du code « faussement-content ». Au lieu de lan­cer une excep­tion, ils pré­fèrent ren­voyer un false et assi­gner un mes­sage d’er­reur dans une pro­prié­té, ce qui force le pro­gram­meur à aller pêcher l’er­reur à l’aide de la méthode get_error().
Uti­li­ser la méthode de « l’heu­reuse excep­tion » est bien plus pro­duc­tif. Si le fra­me­work que vous uti­li­sez pos­sède une classe de ges­tion des excep­tions, il est bien plus simple de l’é­tendre avec vos propres excep­tions et ain­si sim­pli­fier consi­dé­ra­ble­ment le débogage.

class MonException extends Exception {}

10/ Utiliser les bonnes clauses

Il est d’u­sage d’u­ti­li­ser les ins­truc­tions condi­tion­nelles pour faire modi­fier le flux d’exé­cu­tion. Il est ten­tant de tes­ter une condi­tion puis d’exé­cu­ter beau­coup de codes si la condi­tion est vraie et de repor­ter dans le else tout ce qui ne rem­plit pas la condition.

Ce type de solu­tion, pré­sente un risque de « spa­ghet­ti­sa­tion » du code. On peut rendre ce code beau­coup plus lisible si nous ren­ver­sons la condi­tion comme suit :

function uneFonction($param) {
  if ($param != 'OK') return false;
  $this->faitUntruc();
  return true;
}

Cette tech­nique com­bi­née aux deux autres vues pré­cé­dem­ment per­met d’a­voir un code à la fois beau­coup plus élé­gant mais aus­si bien plus facile à entretenir.

11/ Utilisez While pour les itérations simples

La boucle for est uti­li­sée cou­ram­ment quand vous avez besoin d’un comp­teur comme ici :

for (var i = 0; i < x; i++) { ... }

Il y a de très bonnes rai­sons d’utiliser la boucle for ; mais pour des cas aus­si simples, la boucle while est bien plus efficace :

var i = x; while (i--) { ... }

Et tra­di­tion­nel­le­ment 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.

12/ Facilitez-vous l’entretien de vos méthodes

Une méthode rem­plit en géné­ral une fonc­tion simple et limi­ter à une taille rai­son­nable les méthodes est un gage de bon fonc­tion­ne­ment et acces­soi­re­ment d’évolutivité. Voi­ci 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écou­pant ce monstre en uni­tés fonc­tion­nelles plus petites, on s’assure un entre­tien plus facile (et une plus grande uni­ver­sa­li­té du code). C’est une des erreurs les plus com­munes 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();
   }
}

13/ Évitez une trop grande imbrication

Un trop grand nombre d’imbrications dans le code le rendent dif­fi­cile à entre­te­nir, exemple :

function doSomething() {
    if ($someCondition) {
        if ($someOtherCondition) {
            if ($yetSomeOtherCondition) {
                doSomethingSpecial();
            }
            doSomethingElse();
        }
    }
}

En uti­li­sant 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 confron­té à ce type d’imbrication, il y a de fortes chances que votre code puisse être décom­po­sé en uni­té fonc­tion­nelle plus simple :

function someFunc() {
   if($oneThing) {
      $this->doSomething();
      if($anotherThing)
         $this->doSomethingElse();
   }
}

Cette méthode est décom­po­sable 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 confron­té vous-même à une trop grande imbri­ca­tion des if, exa­mi­nez votre code, il se pour­rait que votre méthode veuille faire trop de choses, et vous aurez tout inté­rêt à seg­men­ter 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 imbri­quée et pour lui don­ner une exis­tence propre.

function someFunct() {
   $orderId = 23;
   $selectedProductName = 'superComputer';
   $this->order->set($orderId);
   $this->order->addProduct($selectedProductName);
   $this->shoppingList->add($selectedProductName);
}

14/ Évitez l’utilisation des chiffres et chaînes de caractères “magiques”

Chiffres et chaînes magiques sont sou­vent uti­li­sés comme para­mètres lors d’un appel de fonc­tion ou de méthode sans autre forme d’explication et qui rendent si dif­fi­cile la com­pré­hen­sion du schmil­blick pas seule­ment pour les autres, mais aus­si pour vous pas­sé quelque temps. Au lieu de ceci :

function someFunct() {
   $this->order->set(23);
   $this->order->addProduct('superComputer');
   $this->shoppingList->add('superComputer');
}

Spé­ci­fiez ce que ces chiffres et chaîne veulent dire, pour ce faire, il suf­fit de les assi­gnés à une variable au nom explicite !

function someFunct() {
   $orderId = 23;
   $selectedProductName = 'superComputer';
   $this->order->set($orderId);
   $this->order->addProduct($selectedProductName);
   $this->shoppingList->add($selectedProductName);
}

Cer­tains sacri­fient la créa­tion de variables sur l’hôtel de la per­for­mance ; cepen­dant, rete­nez qu’il faut tou­jours don­ner la prio­ri­té à la lisi­bi­li­té. L’optimisation devient néces­saire quand vous pou­vez dire pour­quoi elle est néces­saire.

15/ Utilisez les fonctions intégrées pour les tableaux

Plu­tôt que d’utiliser des foreach() à répé­ti­tion pour faire des choses élé­men­taires 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 tou­jours simple de prime abord, mais pre­nez le temps de les apprendre, vous y gagne­rez à la longue.

16/ Évitez de sur-utilisez les variables

Mal­gré ce qui a été dit au point n°14, il faut cepen­dant faire atten­tion de ne pas géné­rer des variables inutiles. Regar­dez ce code :

public function get_posts() {
   $query = $this->db->get('posts');
   $result = $query->result();
   return $result;
}

La variable $result est ici tota­le­ment inutile, le code sui­vant est tout aus­si effi­cace et clair :

public function get_posts() {
   $query = $this->db->get('posts');
   return $query->result();
}

Conseils généraux

17/ Reposez-vous sur le moteur de base de données

Une base de don­nées est conçue pour mani­pu­ler des infor­ma­tions, uti­li­sez les outils qu’elle met à votre dis­po­si­tion pour conce­voir des appli­ca­tions plus effi­caces. Par exemple, vous pou­vez réduire le nombre de requêtes redon­dantes dans pas mal de cir­cons­tances. La plu­part des scripts de ges­tion uti­li­sa­teur font au moins deux requêtes pour un enre­gis­tre­ment, une pour véri­fier 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 uti­li­ser les fonc­tions natives de MyS­QL pour la véri­fi­ca­tion puis l’insertion (ou pas).

Moins on code mieux on code.

18/ Nommez correctement vos variables

Le temps où l’on nom­mait les variables xy ou z est révo­lu (à moins que cela ne soit des coor­don­nées spa­tiales ). Les variables repré­sentent une part impor­tante de la logique de votre déve­lop­pe­ment. Si vous ne vou­lez pas taper des noms longs pour vos variables, chan­gez de cré­me­rie. Heu, chan­gez d’IDE ! Les plus modernes d’entre eux pro­posent une com­plé­tion qui réagit au quart de tour même pour les variables.

Codez tou­jours comme si vous le fai­siez dans l’optique de reve­nir sur le code six mois plus tard. Êtes-vous cer­tain que la variable $truc aura encore une signi­fi­ca­tion pour vous dans un an ? Cer­tai­ne­ment pas, donc, soyez descriptif.

19/ Les méthodes sont des actions

Nom­mez vos méthodes avec des verbes repré­sen­tant l’action clé. Le concept est exac­te­ment l’inverse que pour les variables. Uti­li­sez une forme courte, mais des­crip­tive. Pri­vi­lé­giez les méthodes au nom court pour les méthodes publiques et des noms plus long (et donc plus des­crip­tifs) pour les méthodes pri­vées. Ceci devrait aider à lire votre code comme une belle prose. Évi­tez d’utiliser une autre langue que l’anglais pour le code. Pour­quoi ? Si vous déve­lop­pez des plug-in pour Word­Press ou jQue­ry, une quasi-majorité de vos uti­li­sa­teurs seront anglo­phones, chouchoutez-lès. L’anglais est clair et concis, c’est la langue inter­na­tio­nale de la pro­gram­ma­tion, c’est un fait, tenez-en compte ; après si vous vou­lez jouer l’exception cultu­relle, c’est votre choix.

20/ Recommandation de structure

Fina­le­ment, la struc­ture du code est une chose aus­si impor­tante que la lisi­bi­li­té et que toutes les choses que nous venons de voir. Voi­ci donc deux recommandations :

  • inden­tez avec quatre ou deux espaces, plus serait dérai­son­nable et nui­rait cer­tai­ne­ment à la clar­té de l’ensemble.
  • Le para­graphe ori­gi­nal par­lait de la lar­geur de la ligne qu’il est pré­fé­rable de l’imiter à 120 carac­tères de n’avoir pas à scrol­ler pour visua­li­ser tout le code. Je suis per­son­nel­le­ment en accord avec cette recom­man­da­tion ; mais il se trouve que tous les édi­teurs de code modernes pos­sèdent une fonc­tion de world-wrap qui for­mate le code à la lar­geur de la fenêtre de tra­vail ce qui rend ce conseil moins per­ti­nent ; cepen­dant, il n’en demeure pas moins que pour bien coder, il faut avoir aus­si un bon outil. Cet article n’est pas là pour vous aider à choi­sir un bon édi­teur de code ; mais, quel que soit votre choix, appre­nez à vous en servir.

Conclusion

Je n’ai jamais com­mis d’erreur stu­pide” Per­sonne. Jamais.

Ecrit par Sébastien Degliame
Pas de commentaires »

Répondre

Vous devez être connecté pour poster un commentaire.


© 2024 Sébastien DEGLIAME, Creative Commons License