Pages

Decorated pattern

Objectif

l'objectif de cet article est d'améliorer la qualité du code et d'introduire des notions de "design pattern".

En particulier, nous introduirons le pattern "decorated".

Code de base

Nous allons écrire un programme initial.

Ce programme modélise un ensemble de voitures.

Chaque voiture est représentée par sa position.

Voici le code qui déclare un objet littéral et l'affecte à une variable.


myCar est une variable globale qui "symbolise" la voiture.

La mémoire physique.

Voici une représentation sous forme de schéma.

Représentation de l'objet{pos:1}

En mémoire la voiture dispose d'une case mémoire nommée pos pour stoker la position de la voiture sur un axe. L'objet {} possède une propriété pos qui pointe sur un objet représenté lui même par l'objet {1}.

Mais, pour manipuler simplement l'objet ma voiture qui se trouve stockée en mémoire physique de mon ordinateur, mon programme utilise la variable myCar. On dit que myCar pointe sur l'objet ma voiture.

Ajoutons une deuxième voiture et déplaçons les voitures


Nous allons tenter d'améliorer ce code ! 

Création d'une bibliothèque

Nous allons progressivement introduire au sein d'une bibliothèque des améliorations.

Définition d'une fonction : 

A droite : Amélioration du code, création d'une bibliothèque.

Nous améliorons notre code par un appel à la fonction.

La fonction move va regrouper les différentes actions communes à l'ensemble des voitures.


                                                                                    Nous déclarons une variable globale move 
                                                                                      qui pointe sur un objet fonction.

Côté code, nous appelons la fonction move.

Remarque :

mycar.pos++ est remplacé par move(mycar)

L'amélioration n'apparaît pas clairement dans le fichier index.js.

L'intérêt de l'utilisation d'une fonction deviendra évident dans le cas d'un code plus complexe.

Intérêt 1: éviter la recopie du code.

Voici un exemple qui montre l'intérêt de factoriser un grand nombre de lignes de code.


intérêt principal : modification du corps de la fonction.

En cas de modification, les changements seront localisés à un seul endroit.


Design pattern.

Pourquoi ne pas utiliser la technique précédente en écrivant une fonction de création des voitures ? Nous allons ainsi factoriser la création d'objets similaires.

Nous allons découvrir ainsi un "design pattern" très utile. Decorator Functions

Le corps de la fonction doit permettre de modifier des objets pour les faire "ressembler" à des voitures !

On parle de décoration. Comme c'est jolie !


                                                                                Nous donnons le corps de la fonction. Notez bien                                                                                    que pour un cas réel le corps de la fonction serait                                                                                    plus important et l'intérêt de ne pas répéter le code                                                                                  serait évident.



                                                                                Notez que la fonction move a un argument (car)

this !

Modifions maintenant la fonction move pour faire apparaître un terme très employé this.

Nous verrons que l'idée sera de ne plus utiliser une variable comme argument de la fonction move.


Dans la fonction move, this sera lié à l’objet qui est à gauche du point dans l'appel.

Par exemple this est lié, grâce au mécanisme de Javascript à l'objet mycar dans l'appel mycar.move().


Ainsi à l'appel de move( ), on fait correspondre les paramètres aux variables passées (il n'y en a pas ici) et à l'appel du point mycar[.]move() on fait correspondre this à l'objet mycar qui est à gauche du point.





Notez que obj.move pointe sur la fonction, il n'y a donc en mémoire qu'une seule fonction.

Attention au comportement de l'appel de la fonction move() seule. This serait lié à l'objet global windows. Le comportement ne serait surement pas celui attendu.

Encapsulation

Il semble intéressant d'intégrer la fonction move dans la fonction carlike. 

La lecture du code est plus clair et regrouper au sein d'une même fonction.


Mémoire

Mais, quelles sont les implications en mémoire ! Il est important de noter les conséquences de cette intégration.

Rappels :

var makeObj = function () {
   return {};
}
var a = makeObj();
var b = makeObj();
var c = a;

(a === b) // false, a et b sont comme des jumeaux mais aussi des personnes différentes
(a === c) // true

Remarque : les résultats de comparaison sont les mêmes si la fonction retourne une fonction à la place d'un objet.

La conséquence principale est donc la duplication du code de la fonction move (et donc de toutes les fonctions) pour chaque création d'objet.

Revenons une dernière fois au code. Un des avantage à inclure le code de la fonction move à l’intérieur de la fonction carlike et que nous pouvons nous servir de la closure.

Grace à la closure, après avoir exécuté la fonction carlike, la fonction move aura encore accès à la variable obj qui est passé en argument de carlike.

Comparer le code initial à ce code :