Modèles de conception JavaScript: Le modèle d’observateur

En JavaScript, il y a un problème qui se pose souvent. Vous avez besoin d’un moyen de mettre à jour des parties d’une page en réponse à certains événements, avec les données qu’elles fournissent. Supposons, par exemple, une entrée utilisateur que vous projetez ensuite dans un ou plusieurs composants. Cela entraîne beaucoup de push-and-pull dans le code pour tout synchroniser.

C’est là que le modèle de conception d’observateur peut aider — il permet la liaison de données un à plusieurs entre les éléments. Cette liaison de données unidirectionnelle peut être pilotée par des événements. Avec ce modèle, vous pouvez créer du code réutilisable qui répond à vos besoins spécifiques.

Dans cet article, j’aimerais explorer le modèle de conception de l’observateur. Cela vous aidera à résoudre un problème courant que vous voyez dans les scripts côté client. Il s’agit d’une liaison de données un à plusieurs, unidirectionnelle et basée sur des événements. C’est un problème qui survient souvent lorsque vous avez de nombreux éléments qui doivent être synchronisés.

J’utiliserai ECMAScript 6 pour illustrer le modèle. Oui, il y aura des classes, des fonctions de flèche et des constantes. N’hésitez pas à explorer ces sujets par vous-même si vous n’êtes pas déjà familier. J’utiliserai des parties d’ES6 qui introduisent uniquement du sucre syntaxique, donc il est portable avec ES5 si nécessaire.

Et j’utiliserai le développement piloté par les tests (TDD) pour travailler sur le modèle. De cette façon, vous avez un moyen de savoir à quel point chaque composant est utile.

Les nouvelles fonctionnalités de langage dans ES6 permettent un code succinct. Alors, commençons.

L’observateur d’événements

Une vue de haut niveau du motif ressemble à ceci:

EventObserver│ ├── subscribe: adds new observable events│ ├── unsubscribe: removes observable events|└── broadcast: executes all events with bound data

Après avoir étoffé le modèle d’observateur, j’ajouterai un nombre de mots qui l’utilise. Le composant compte de mots prendra cet observateur et rassemblera tout cela.

Pour initialiser le do EventObserver:

class EventObserver { constructor() { this.observers = ; }}

Commencez par une liste vide d’événements observés, et faites-le pour chaque nouvelle instance. À partir de maintenant, ajoutons plus de méthodes à l’intérieur de EventObserver pour étoffer le modèle de conception.

La méthode Subscribe

Pour ajouter de nouveaux événements fait:

subscribe(fn) { this.observers.push(fn);}

Récupérez la liste des événements observés et poussez un nouvel élément dans le tableau. La liste des événements est une liste de fonctions de rappel.

Une façon de tester cette méthode en JavaScript simple est la suivante:

// Arrangeconst observer = new EventObserver();const fn = () => {};// Actobserver.subscribe(fn);// Assertassert.strictEqual(observer.observers.length, 1);

J’utilise des assertions de nœud pour tester ce composant dans le nœud. Les mêmes assertions existent également que les assertions Chai.

Notez que la liste des événements observés se compose d’humbles rappels. Nous vérifions ensuite la longueur de la liste et affirmons que le rappel est sur la liste.

La méthode de désabonnement

Pour supprimer les événements fait:

unsubscribe(fn) { this.observers = this.observers.filter((subscriber) => subscriber !== fn);}

Filtrez de la liste tout ce qui correspond à la fonction de rappel. S’il n’y a pas de correspondance, le rappel reste sur la liste. Le filtre renvoie une nouvelle liste et réassigne la liste des observateurs.

Pour tester cette belle méthode, faites:

// Arrangeconst observer = new EventObserver();const fn = () => {};observer.subscribe(fn);// Actobserver.unsubscribe(fn);// Assertassert.strictEqual(observer.observers.length, 0);

Le rappel doit correspondre à la même fonction que celle de la liste. S’il y a une correspondance, la méthode de désabonnement la supprime de la liste. Remarque le test utilise la référence de fonction pour l’ajouter et la supprimer.

La méthode de diffusion

Pour appeler tous les événements do:

broadcast(data) { this.observers.forEach((subscriber) => subscriber(data));}

Cela parcourt la liste des événements observés et exécute tous les rappels. Avec cela, vous obtenez la relation un-à-plusieurs nécessaire aux événements souscrits. Vous passez le paramètre data qui rend les données de rappel liées.

ES6 rend le code plus efficace avec une fonction de flèche. Notez la fonction (subscriber) => subscriber(data) qui effectue la majeure partie du travail. Cette fonction de flèche à une ligne bénéficie de cette courte syntaxe ES6. C’est une amélioration certaine du langage de programmation JavaScript.

Pour tester cette méthode de diffusion, faites:

// Arrangeconst observer = new EventObserver();let subscriberHasBeenCalled = false;const fn = (data) => subscriberHasBeenCalled = data;observer.subscribe(fn);// Actobserver.broadcast(true);// Assertassert(subscriberHasBeenCalled);

Utilisez let au lieu de const afin que nous puissions modifier la valeur de la variable. Cela rend la variable mutable, ce qui me permet de réaffecter sa valeur à l’intérieur du rappel. L’utilisation d’un let dans votre code envoie un signal aux autres programmeurs que la variable change à un moment donné. Cela ajoute de la lisibilité et de la clarté à votre code JavaScript.

Ce test me donne la confiance nécessaire pour m’assurer que l’observateur fonctionne comme prévu. Avec TDD, il s’agit de construire du code réutilisable en JavaScript simple. Il y a des avantages à écrire du code testable en JavaScript simple. Testez tout et conservez ce qui est bon pour la réutilisation du code.

Avec cela, nous avons étoffé le EventObserver. La question est, que pouvez-vous construire avec cela?

Le Modèle d’Observateur en action: Une démo du Nombre de mots du Blog

Pour la démo, il est temps de mettre en place un article de blog où il garde le nombre de mots pour vous. Chaque frappe que vous entrez en tant qu’entrée sera synchronisée par le modèle de conception de l’observateur. Pensez-y comme une entrée de texte libre où chaque événement déclenche une mise à jour là où vous en avez besoin.

Pour obtenir un nombre de mots à partir de la saisie de texte libre, on peut faire:

const getWordCount = (text) => text ? text.trim().split(/\s+/).length : 0;

C’est fait ! Il se passe beaucoup de choses dans cette fonction pure apparemment simple, alors que diriez-vous d’un humble test unitaire? De cette façon, il est clair ce que je voulais faire:

// Arrangeconst blogPost = 'This is a blog \n\n post with a word count. ';// Actconst count = getWordCount(blogPost);// Assertassert.strictEqual(count, 9);

Notez la chaîne d’entrée quelque peu loufoque à l’intérieur de blogPost. J’ai l’intention que cette fonction couvre autant de cas de bord que possible. Tant que cela me donne un compte de mots correct, nous nous dirigeons, en fait, dans la bonne direction.

En note de côté, c’est le vrai pouvoir de TDD. On peut itérer sur cette implémentation et couvrir autant de cas d’utilisation que possible. Le test unitaire vous indique comment je m’attends à ce que cela se comporte. Si le comportement a un défaut, pour une raison quelconque, il est facile de l’itérer et de le modifier. Avec le test, il reste suffisamment de preuves pour que toute autre personne puisse apporter des modifications.

Il est temps de câbler ces composants réutilisables au DOM. C’est la partie où vous pouvez manier du JavaScript simple et le souder directement dans le navigateur.

Une façon de le faire serait d’avoir le code HTML suivant sur la page:

<textarea placeholder="Enter your blog post..." class="blogPost"></textarea>

Suivi de ce JavaScript:

const wordCountElement = document.createElement('p');wordCountElement.className = 'wordCount';wordCountElement.innerHTML = 'Word Count: <strong>0</strong>';document.body.appendChild(wordCountElement);const blogObserver = new EventObserver();blogObserver.subscribe((text) => { const blogCount = document.getElementById('blogWordCount'); blogCount.textContent = getWordCount(text);});const blogPost = document.getElementById('blogPost');blogPost.addEventListener('keyup', () => blogObserver.broadcast(blogPost.value));

Prenez tout votre code réutilisable et mettez en place le modèle de conception de l’observateur. Cela suivra les changements dans la zone de texte et vous donnera un nombre de mots juste en dessous. J’utilise le body.appendChild() dans l’API DOM pour ajouter ce nouvel élément. Ensuite, attachez les auditeurs de l’événement pour lui donner vie.

Remarque avec les fonctions de flèche, il est possible de câbler des événements à une ligne. En fait, vous diffusez des modifications axées sur les événements à tous les abonnés avec cela. Le () => blogObserver.broadcast() fait l’essentiel du travail ici. Il passe même les dernières modifications de la zone de texte directement dans la fonction de rappel. Oui, les scripts côté client sont super cool.

Aucune démo n’est complète sans celle que vous pouvez toucher et modifier, ci-dessous le CodePen:

Voir le stylo Le motif Observateur par SitePoint (@SitePoint) sur CodePen.

Maintenant, je n’appellerais pas cette fonctionnalité complète. Ce n’est qu’un point de départ du modèle de conception de l’observateur. La question dans mon esprit est, jusqu’où êtes-vous prêt à aller?

Pour l’avenir

C’est à vous de pousser cette idée encore plus loin. Il existe de nombreuses façons d’utiliser le modèle de conception d’observateur pour créer de nouvelles fonctionnalités.

Vous pouvez améliorer la démo avec:

  • Un autre composant qui compte le nombre de paragraphes
  • Un autre composant qui affiche un aperçu du texte entré
  • Améliorez l’aperçu avec le support de markdown, par exemple

Ce ne sont que quelques idées que vous pouvez faire pour approfondir cela. Les améliorations ci-dessus mettront au défi vos côtelettes de programmation.

Conclusion

Le modèle de conception observer peut vous aider à résoudre des problèmes réels en JavaScript. Cela résout le problème permanent de garder un tas d’éléments synchronisés avec les mêmes données. Comme c’est souvent le cas, lorsque le navigateur déclenche des événements spécifiques. Je suis sûr que la plupart d’entre vous ont maintenant rencontré un tel problème et ont couru vers des outils et des dépendances tierces.

Ce modèle de conception vous permet d’aller aussi loin que votre imagination est prête à aller. En programmation, vous abstrayez la solution dans un modèle et créez du code réutilisable. Il n’y a pas de limite à la distance que cela vous mènera.

J’espère que vous verrez combien, avec un peu de discipline et d’effort, vous pouvez faire en JavaScript simple. Les nouvelles fonctionnalités du langage, telles que ES6, vous aident à écrire du code succinct réutilisable.

Cet article a été examiné par des pairs par Giulio Mainardi. Merci à tous les pairs examinateurs de SitePoint d’avoir rendu le contenu de SitePoint le meilleur possible !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.