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!

Agile Tour 2014 - Une mise en production à chaque heure de la journée

Publié par Jean-Nicolas Viens le mardi 11 novembre 2014 à 00:00

Description

L’intégration continue c’est bien, mais le déploiement continu c’est mieux! Le gain est évident : une boucle de rétroaction encore plus rapide. Les dangers le sont également : les outils et tableaux de bords doivent être digne de confiance. La qualité doit également être au rendez-vous. Comment diminuer ces risques et aller chercher ce gain en rétroaction est le sujet que nous allons explorer.

  • Présentateur: Jean-Nicolas Viens
  • Niveau : Intermédiaire
  • Public cible : Développeurs et architectes

Contenu

Avoiding Layers of Misdirection

Publié par Jean-Nicolas Viens le jeudi 29 mai 2014 à 09:53

Have mocks led you to add frustrating layers of indirection? You are not alone. This is an argument part of the ongoing debate with DHH (David Heinemeier Hansson), Martin Fowler and Kent Beck.

All three of them seem to be in agreement on one thing: mocks should be avoided. This is what I think brings you from layers of indirection to layers of misdirection.

Skippable summary

You may skip this section, even if you are not familiar with the topic. Refer to these links to continue the discussion!

Here are the main parts (I think), in order :

  • Is TDD Dead? David's blog post following his railsconf keynote. The subsequent articles are interesting as well.
  • Their firstsecond and third hangout sessions.
  • Google, twitter

These people have a lot more experience than I do, but I still don't see mocks the same way they do. Hopefully, this will make some sense. As for everything pertaining to this debate : what works for me might not work for you. But, it doesn't mean you should not try it.

This is obviously a vast debate with various angles of approach. If you want test isolation with proper mocking, Gary Bernhardt nailed it in my opinion. Read his post. If the test speed argument is one that bothers you, I suggest you read Corey Haines' article. If you are still unsure that integrations tests can harm you when not done properly, I could not explain it better than J. B. Rainsberger in this video.

What if mocks had nothing to do with it

The angle I want to tackle is one that has confused me a lot over the years. Sorry, there will be no definite answer at the end of this article, just a suggestion for a further conversation. I hope you chip in!

Over the years, I have seen people hate TDD because they had to do mocks. Doing mocks forced them to create new classes. Many of them got confused in their own design after three or four of these classes.

This phenomenon is also seen when you read someone else's code. If it's all in one place, you can read it in one go. Adding layers of indirection makes it harder to follow. DHH puts it this way :

[If I can look at something that fits within 10 lines of clean code], to me that's endlessly easier to understand than sixty lines that has three additional layers of indirection that I have to comprehend first.

I agree with this statement, up until he says "that I have to comprehend first". You don't. The name of the class or method you are calling should be enough to tell you what is happening there. You know the what, not the how. To me this is key to making complex business models work. I never add layers of indirection, I add layers of I-don't-care-ness.

But this is where I get confused. This makes sense to me, but most people seem to disagree. Everyone wants to understand every single layer first, but that is just impossible to do when you live in a world of complex business rules (as opposed to simple CRUD operations).

Maybe this is just a naming problem? Of course, if a method is poorly named, this will become a layer of misdirection. But I think there is more to it.

What if you just made it hard on yourself

Just let go.

Adding a layer, or simply a private method, allows me to understand a small portion of the algorithm faster than anything else. When you write a complex algorithm without tests, what do you do? Write a part of it, refresh, does it work? Yes, keep going. You iterate. Test the algorithm bit by bit. Adding layers allows you to do just that, in a TDD flow that makes sure you get every part of the algorithm right.

Here is a simple example. What does this do?


receipt = new Receipt(employeeNumber);
for(int i = 0; i < items.size; i++) {
    receipt.registeredItems.add(items[i]);
}
return receipt;

You can most likely figure it out, but this version should be faster to understand :


receipt = receiptFactory.create(employeeNumber);
receipt.registerItems(items);

What happened? Did I really remove code? Not at all. In fact, I would have added code. The number of lines of code pertaining to the business logic would be the same, but code to make the compiler happy would be added. That is class declaration, brackets, etc. It would even add one more file. 

Is it still worth it? Hell yeah! I've now stopped caring about two things: how to create a receipt and how to add items to it. Do you need to queue the new receipt somewhere? Don't know don't care. Do you need to add something between each items? Don't know don't care. Our brain is very limited, asking less of it each time is good. You get more lines of code, but less code to care about for each step: you brain is happy.

Tests get better too. I can now have a test that says "you should create a receipt and add items to it" (hint: mocks required!). Obviously, the factory will now have to be tested: is the receipt created properly? Same goes for the receipt itself. 

I added a layer of indirection, but the resulting algorithm is simpler, in each and every one of these classes. +1 for the readability because you know where to look if you want to change how a receipt is created. If you don't care, you don't even need to look for it. +1 for test documentation too, I now know they fully document these pieces of code.

This code is not tested!

The code above has not been tested. It looks like java, but who cares.

If I was to do it in TDD, I would probably have come up with better names, better collaborators. I have found that the best way to figure out the proper use of layers is through (mockist) TDD.

If a mock is hard to reason about, it is wrong. An integration test will not put enough pressure on my design to let me know about it. The slice which is being tested will grow, and grow, and grow... until the "in-between" becomes unreadable. What do we do? We split it! We add a layer of I-don't-care-ness, or at least that's what we think we are doing. We are using the code immediately, so it looks easy enough to understand. Here is the catch: how do you know you made the right separation? Is it viable in the long term?

This is where I leave it up to you

What do you think? Do you have to understand each parts before changing an algorithm? How does it add complexity?

I know the example is an over simplification, but in my experience, it scales properly.

Am I the only one to imagine software in my head and see all the moving parts interacting with each other? No I am not crazy, my mother had me tested - Sheldon Cooper.

Archive