Bienvenue sur Développement Agile

Référence sur le développement logiciel Agile. Nous traitons de conception, de programmation, de pratiques de génie logiciel, d'essais et d'autres sujets connexes.

L'orienté objet en JavaScript, cheat sheet

Publié par Jean-Nicolas Viens le mercredi 17 décembre 2014 à 20:05

Avant de vous donner cette fameuse cheat sheet, qui saura être utile je l'espère, je voudrais vous montrer deux dernières possibilités avec les objets: les méthodes statiques et comment il est possible de manipuler le mot-clé this pour changer sa valeur avant d'invoquer une fonction.

Méthodes statiques

Ça ne faisait pas parti de l'exemple initial, mais ça peut être intéressant!

Comment faire une méthode statique? C'est simple, il faut juste se rappeler que tout est un objet! Donc on peut écrire :

var Cart = function() { ... }
Cart.methodeStatique = function() { return "méthode statique"; }

Un seule attrape : on ne peut pas invoquer une méthode statique sur un objet, on doit passer par le nom de la classe (i.e. Cart.methodeStatique(), et non uneinstanceDeCart.methodeStatique()).

Fait intéressant : essayez de faire afficher this à l'intérieur d'une fonction statique. Étrange non? Il y a une utilité à cela, je vous laisse y réfléchir!

Manipuler la valeur du this

Vous êtes-vous déjà demandé comment jQuery fait pour que, magiquement, this contienne l'élément du DOM lors d'événements? Par exemple:

$('.element').on('click', function() {
  // this prend la valeur de l'élément cliqué
});

Il y a trois fonctions disponibles en JavaScript pour arriver à cette fin.

Les deux premières sont très semblables; call prend une liste de paramètres, alors que apply prend un tableau d'arguments. La différence est mince, mais selon le cas d'utilisation une version peut s'avérer plus utile que l'autre. Par contre, dans les deux cas le premier argument est toujours la valeur qui sera associée à this! C'est de cette façon que jQuery manipule la valeur du this et permet ainsi un API plus concis.

Une autre fonction nommée bind permet d'associer des arguments (y comprit this) à une fonction, sans l'invoquer. C'est plutôt difficile au début de voir l'utilité de cette technique, mais sachez qu'elle est bien documentée dans le monde des langages fonctionnels sous le nom de curried functions ou currying. Ce concept est parfois également appelé partial application dépendamment du langage; ce sont deux concepts similaires, mais différents.

Manipuler la valeur du this est à faire avec précaution! Vous pourriez surprendre votre utilisateur, votre collègue et vous-même.

Cheat sheet

Je ne suis pas souvent en faveur des bouts de code à copier/coller sans prendre le temps de les comprendre, mais ici il y a beaucoup de code de base requis pour rien.

Je vous offre donc exceptionnellement cette cheat sheet, mais si vous essayez de faire quelque chose et que ça ne fonctionne pas, prennez le temps de relire cet article pour bien comprendre.

var Classe = (function() {

  var constanteOuStaticPrivee;

  function Classe() {
    this.public = 1;
    this._prive = 2;
  }

  Classe.prototype.fonctionPublique = function() { 
    return this.publique + this._prive;
  }

  Classe.fonctionStatic = function() {}

  return Classe;

})();

Simplement copier/coller ce bout de code et faire un rechercher/remplacer du mot Classe par le nom de votre classe! Encore mieux, il est possible d'en faire un "live template" dans la majorité des IDE modernes.

Exercice

Dans un callback jQuery, on sait que this vaut l'élément du DOM sur lequel l'événement est arrivé. Par exemple 

$('#element').click(function() {
  this.tagName; // Le nom du tag HTML, vu que this = l'élément du DOM
});

Dans ce cas, proposez une solution qui permet d'avoir accès aux attributs de notre objet puisque this ne peut pas valoir deux choses à la fois! Voici un exemple de ce qui ne fonctionne pas :

var Classe = (function() {

  function Classe() {
    this.message = "salut!";
  }

  Classe.prototype.fonction = function() {
    $('#element').click(function() {
      // 'this' ne peut pas valoir 2 choses en même temps!
      // Ici 'this' est l'élément du DOM, donc this.message est "undefined"
      $(this).text(this.message);
    });
  };

  return Classe;
})();

Conclusion

Essayez ces technique et dites-moi si votre code s'en porte mieux! N'oubliez pas que ces techniques d'orienté objet facilitent grandement les tests, avec Jasmine.js par exemple.

L'orienté objet en JavaScript, aller plus loin

Publié par Jean-Nicolas Viens le lundi 15 décembre 2014 à 07:08

Maintenant que nous avons solidifié quelques bases en JavaScript, il est temps de créer notre premier objet! Commençons par ce mot-clé souvent mal compris et qui cause bien des maux de tête : le mot-clé this. Ne vous inquiétez pas, le prochain billet contiendra une "cheat sheet" vous permettant de facilement revoir ces concepts!

Mot-clé: this

La façon la plus simple de voir this c'est de se dire qu'il est le propriétaire de la fonction qui est invoquée.

Par exemple :

var cart = function() {
  this.allo = "allo";
}

cart();

console.log(allo); // contient la chaine "allo";

Donc allo est disponible autour de la fonction cart(), car ici this == window (le propriétaire le plus haut niveau possible et par défaut dans le navigateur).

On pourrait donc essayer d'encapsuler ça dans une autre fonction :

(function() {

  var cart = function() {
    this.allo = "allo";
  }

  cart();

  console.log(allo); // contient la chaine "allo";
})(); // IIFE

console.log(allo); // contient la chaine "allo";

console.log(cart); // undefined

Hmm, allo est toujours défini dans window. La portée (scope) est bien respectée : cart n'est pas défini en dehors de la première fonction, mais allo reste défini sur window.

Par contre, si on était en mode strict (avec "use strict";), this serait undefined. Sans le mode strict et puisque this n'est associé à rien le navigateur utilise window par défaut.

Il y a bien sûr moyen d'associer this à une instance et non à window et c'est que nous allons voir.

Mot-clé: new

L'utilité du mot-clé new est d'assigner this à l'objet qu'on est en train de créer. Par convention, les fonctions sur lesquelles on peut exécuter new commencent par une majuscule. Par exemple :

var Cart = function() {
  this.allo = "allo";
  console.log(this); // log : Cart {allo: "allo"} au lieu de Window { ... }
}

var aCart = new Cart();
console.log(aCart.allo); // "allo"
console.log(allo); // undefined.

On dit donc que Cart est un constructeur.

Vie privée 

Par contre, on ne peut toujours pas définir de champ ou méthodes privées avec ce que nous avons. On pourrait utiliser le mot-clé var dans le constructeur, mais aucune des autres méthodes (celles qui sont dans le prototype) ne pourraient voir cette variable. On pourrait tout partager via this, mais alors tout serait public.

En fait, il n'y a pas vraiment de façon d'implémenter un champ privé dans un objet en JavaScript. La seule chose qui change entre les objets est le this. On a donc deux choix :

Idée #1: tout englober dans une fonction (IIFE) supplémentaire afin de créer une nouvelle portée (donc pouvoir utiliser var). Cette idée fonctionne bien, mais tout ce qui est déclaré sans le this et à l'intérieur de la fonction sera partagé entre chaque instance. Dans cet exemple, c'est la variable prive qui est partagée entre les instances a et b :

var Classe = (function() {
  var prive;

  function Classe() {
  }

  Classe.prototype.get = function() { 
    return prive;
  }

  Classe.prototype.set = function(uneValeur) { 
    prive = uneValeur;
  }

  return Classe;
})();
  • >> a = new Classe()
  • Classe {get: function, set: function}
  • >> b = new Classe()
  • Classe {get: function, set: function}
  • >> a.set(4)
  • >> b.get()
  • 4
  • >> b.set(10)
  • >> a.get()
  • 10

Quand on invoque le set() sur a alors le get() de b est affecté. Pas bien pratique pour garder un état. C'est cependant parfait pour définir des fonctions utilitaires à la classe (i.e. méthodes privées qui ne modifient pas l'état directement).

Idée #2: on invente une convention. En JavaScript, tout ce qui commence par un souligné (underscore) est considéré comme privé ou un API interne. C'est la solution qui est la plus souvent utilisée.

Et pour terminer...

Voici la classe initiale :

var Cart;

Cart = (function() {
  function Cart() {
    this.items = [];
  }

  Cart.prototype.addItem = function(item) {
    return this.items.push(item);
  };

  return Cart;

})();

Dans ce code , tout est englobé dans une fonction pour obtenir une portée (scope) privée. Tout ce qui est défini dans cette portée n'en sortira pas, sauf via le return.

Le "punch" de la fin est de retourner le constructeur! Il n'est pas invoqué, juste retourné; c'est ce qui fait que la variable à l'extérieur (le premier Cart) agit comme le constructeur qu'elle contient, mais possède une portée privée.

Attention! Le Cart à la ligne 1 et 3 n'est pas le même qu'à la ligne 4, 8 et 12. On donne simplement le même nom au constructeur qu'à la fonction qui l'entoure par convention.

Vous devriez maintenant pouvoir expliquer chaque ligne de cette classe, à la parenthèse près! Il nous reste par contre quelques sujets supplémentaires qui peuvent s'avérer intéressants à explorer, alors allons-y!

En conclusion

J'espère que ce billet explique bien toutes les techniques requises pour arriver à la solution générée par coffeescript. Ce n'est pas si simple, j'en conviens, alors n'hésitez pas à expérimenter dans un REPL! Vous pouvez également consulter le prochain billet pour un dernier sujet un peu plus poussé, mais surtout une "cheat sheet" récapitulant tout ce que nous avons vu.

Vous pouvez également essayer avec TypeScript si vous préférez. Vous obtiendrez un résultat similaire! Essayez-le ici : http://www.typescriptlang.org/Playground

Voici la version TypeScript :

class Cart {
    items: string[];

    constructor() {
        this.items = [];
    }

    addItem(item: string) {
        this.items.push(item)
    }
}

Si vous avez quelques minutes, n'oubliez pas de remplir ce sondage sur notre formation dédiée aux technologies client (frontend). Vos suggestions aideront à mieux préparer cette formation en fonction de vos besoins!

L'orienté objet en JavaScript et questions fréquentes

Publié par Jean-Nicolas Viens le vendredi 12 décembre 2014 à 06:10

Avez-vous déjà créé un objet en JavaScript?

Ça semble anodin pour un langage où "tout est un objet", mais en réalité cet exercice demande de bien comprendre plusieurs concepts fondamentaux du JavaScript. Ce n'est rien de bien compliqué, il suffit de s'y arrêter quelques instants pour bien tout comprendre.

Ce billet est le premier de trois dans lequel nous couvrirons tous les concepts JavaScript requis pour créer un objet : de la porté des variables à la création d'objet.

Nous nous baserons sur le code généré par coffeescript, qui est un langage qui génère du JavaScript, mais avec une syntaxe plus simple. Le but est de comprendre pourquoi coffeescript génère ce code.

Vous pouvez utiliser ce convertisseur pour convertir les exemples suivants entre le JavaScript et le coffeescript. Avec jsfiddle vous pouvez exécuter ces exemples facilement (autant ceux en JavaScript que ceux en coffeescript).

Voici la classe telle que déclarée en coffeescript :

class Cart
  constructor: ->
    @items = []
  
  addItem: (item) ->
    @items.push item

Et voici le code JavaScript généré :

var Cart;

Cart = (function() {
  function Cart() {
    this.items = []; 
  }

  Cart.prototype.addItem = function(item) {
    return this.items.push(item);
  };

  return Cart;

})();

Il y a plusieurs concepts à comprendre avant de bien voir l'utilité de ce code. Nous procéderons étape par étape séparées sur 3 billets de blogue.

Pour référence complète, voici les liens vers les différents concepts :

Portée d'une variable

Premièrement, il faut se souvenir qu'une variable est globale si elle est déclarée sans le mot-clé var. Autrement, sa portée est la fonction qui l'englobe.

function foo() {
  var a = 2;
  b = 4;
}

console.log(b); // Erreur: variable inexistante.

foo();
console.log(b); // Après l'appel de foo(), b = 4!
console.log(a); // Erreur: variable inexistante.

Un cas souvent pathologique est la boucle for :

function foo() {
  for(i = 0; i <=10; i++ {
  }
}

console.log(i); // 10!

Le plus grand danger présentement est "d'écraser" une valeur globale avec une autre, ce qui est évidement très difficile à trouver comme bogue. Pour l'instant le JavaScript est à toute fin pratique "single thread", donc on a pas de problème de ce côté là. Par contre, il n'est pas dit que ce sera toujours ainsi (j'anticipe déjà les projets de 1000 JP pour corriger tout ça!). Mais on s'égare, revenons au code et regardons ce qu'est une IIFE.

IIFE et portée

Une IIFE (Immediatly invoked function expression) est la déclaration d'une fonction qui est immédiatement invoquée. Remarquez bien les parenthèses qui entourent la fonction. Voici un exemple :

var total = (function() {
  var ajouterUn = function(nombre) {
    return nombre + 1;
  }
  return ajouterUn(2);
})();

On a le même résultat que si ajouterUn était déclaré à l'extérieur, mais ici la fonction ajouterUn devient une méthode privée. Impossible de l'invoquer après que total soit calculé puisque ajouterUn est déclaré avec le mot-clé var. Sa portée (scope) est donc la fonction qui l'englobe.

Les fonctions au premier plan

En JavaScript, une fonction est un type comme un autre.

On peut la stocker dans une variable :

var direAllo = function() { return "Allô!"; }

Ce qui est identique à :

function direAllo() {  return "Allô!"; }

Ou la retourner :

var direAllo = function() {
  return function() {
    return "Allô!";
  }
}

console.log(direAllo()()); // les doubles ()() ne sont pas une erreur! On invoque la fonction retournée.

Et on peut combiner les deux :

var direAllo = function() {
  var direAlloJohn = function() {
    return "Allô, John!";
  }

  return direAlloJohn;
}

Dans ce cas, direAllo() sera une fonction (la même que direAlloJohn). Il faut noter par contre que la fonction direAlloJohn n'aura jamais été exposée publiquement (en dehors de la fonction qui l'encapsule).

Prototype

Le prototype contient une série de méthodes à copier dans chaque nouvel objet. C'est un gabarit (template) qui dicte comment créer un objet. On pourrait donc ajouter à l'exemple précédent :

var Cart = function() {
  this.allo = "Allô!";
}

Cart.prototype.salut = function() {
  console.log("1: " + this.allo);
  return "Salut!";
}

var aCart = new Cart();
console.log("2: " + aCart.allo);
console.log("3: " + aCart.salut()); 

Le résultat sera, dans l'ordre :

  • 2: Allô!
  • 1: Allô!
  • 3: Salut!

Le constructeur et les méthodes dans le prototype partagent le même objet `this`, qui est unique et indépendant entre chaque objet (à chaque instanciation). On en apprendra d'avantage sur les constructeurs et le mot-clé `this` dans le prochain article!

Conclusion

Nous avons vu trois concepts de base du JavaScript qui ne sont pas exactement reliés à l'orienté objet, mais qui nous serviront très prochainement.

Pour la suite, nous verrons :

  • Comment utiliser le mot-clé `this`
  • Comment utiliser le mot-clé `new`
  • Faire des méthodes statiques

Entre temps, je vous conseille fortement d'expérimenter avec les concepts tirés des langages fonctionnels. Bien que JavaScript ne soit pas réellement un langage fonctionnel, la possibilité de traiter des fonctions comme des données a des répercussions importantes. Entre autre, vous pouvez essayer de mieux comprendre les promises de jQuery.

Cet article vous a plu? Sachez que nous préparons une formation dédiée aux technologies client (frontend). Nous aimerions votre opinon afin de mieux cibler les besoins des développeurs web et nous apprécierions grandement que vous répondiez à ce court sondage. N'hésitez pas à y écrire tout ce qui pourrait vous intéresser!

Classique du développement logiciel: Head First Design Patterns

Publié par David Beaumier le mercredi 10 décembre 2014 à 13:06

J’ai remarqué récemment que le livre «Head First Design Patterns » a reçu une mise à jour pour souligner son 10ième anniversaire de publication. Premièrement, il faut savoir que ce n’est en aucun cas le livre le plus avancé sur le sujet, mais comme le dit cet internaute « Si tu ne comprends pas les Design Patterns après cela, c'est alors peine perdue ».

Couverture Head First Design Patterns

Il s’agit donc d’un livre tout indiqué pour le p’tit nouveau de votre équipe ou pour un collègue qui ne connait pas encore les patrons de conception et qui souhaite s’initier au sujet. Présenté dans le format ludique associé à la série Head First (approche néanmoins très sérieuse), c’est une lecture agréable et somme toute légère si on considère le sujet. Le livre propose tout d’abord une introduction au concept de « patrons de conception », sans prendre pour acquis que le lecteur est un programmeur OO ceinture noire. Les principes OO liés aux patrons sont présentés au fur et à mesure, de façon à ce que le lecteur possède les bases requises pour bien comprendre comment mettre en oeuvre le patron.

Un des aspects intéressants de ce livre est l’abondance d’illustrations. Celle qui suit est un exemple typique du style utilisé par l’auteur, qui, il faut l’avouer, est assez différent de celui retrouvé dans la majorité des livres traitant du même sujet.

Exemple Diagramme HFDP

Ce classique propose, pour son édition renouvelée, des exemples basés sur les nouveautés de Java 8. Ceci dit, il est tout aussi pertinent pour ceux qui développent sous d’autres plateformes, telle que .NET. Les exemples sont simples et faciles à comprendre.

 Il est impressionnant de voir que plus de 10 ans après sa parution, il se retrouve au #1 des ventes de sa catégorie (au début de décembre 2014). C’est vraiment ce qu’on appelle un incontournable!

Ranking HFDP

Version française

Une édition française de ce livre a déjà existée, avec le titre « Design patterns : Tête la première », mais elle était basée sur l’édition originale et n’était disponible qu’en e-book (PDF). L’éditeur ayant fermé ses portes depuis, elle ne semble plus disponible pour achat.

D’autres billets qui pourraient vous intéresser

Pour aller plus loin

Si vous souhaitez approfondir le sujet des patrons de conception, je vous recommande la formation Concepts orientés-objet avancés appliqués au développement agile présentée par mon collègue Félix-Antoine Bourbonnais.

Copy Driven Development

Publié par Jean-François Gilbert le lundi 8 décembre 2014 à 00:00

Copy Paste

On l'a tous déjà fait : on ne sait pas trop comment utiliser un framework X ou encore on ne se souvient plus tellement comment implémenter un algorithme. Alors on cherche sur Bing (je blague !) et on copie le code dans notre logiciel. À première vue, c'est un gain de productivité fabuleux ! Mais lorsqu'on élève cette pratique au rang de méthode de travail, ça devient carrément du "Copy Driven Development". Et c'est dangereux !

Qu’y a-t-il de mal de mal à copier du code trouvé sur internet ? Quelqu'un d'autre s'est déjà tapé le sale boulot et c'est souvent du code spécialisé dont on ne se sert presque jamais. Je serais tout à fait d'accord si les développeurs prenaient le temps de comprendre ce que le code copié fait et comment ça fonctionne réellement. Notez-bien que je m'inclue dans le lot. Je plaide coupable d'avoir pratiquée le Copy Driven Development à plusieurs reprises. Mais j'essaie de me réhabiliter, votre honneur !

Le problème c'est que quand le code trouvé est copié dans votre projet, il devient VOTRE code. Si ça ne fonctionne pas comme prévu, s'il y a des bugs qui surgissent, serez-vous en mesure de les régler ? Si on le modifie, êtes-vous capable de garantir que ça va continuer de fonctionner comme prévu ? Est-ce que vous avez des tests unitaires qui valident son bon fonctionnement ? 

Je ne dis pas qu'on ne devrait jamais copier de code, mais je crois qu'il est de notre devoir de se l'approprier et d'en comprendre les subtilités. Selon moi, il doit servir d'outil d'apprentissage plutôt que de béquille à notre mémoire défaillante ou notre paresse intellectuelle.

Archive