Introduction
Le design pattern (patron de conception) Observateur est omniprésent de nos jours. Que ce soit en programmation événementielle, avec les architectures MV* style Angular et la problématique du data-binding, la programmation dite reactive ou encore en conjonction avec l'approche Entity-Component-Systems.
Ce billet n'a pas pour ambition de vous expliquer en détail le design pattern Observateur, ce pourrait faire l'objet d'un article indépendant, mais de vous présenter une petite bibliothèque de mon cru qui implémente ce design pattern de façon légèrement différente de ce qui est usuel de rencontrer.
Cette bibliothèque nommée Paon (comme l'oiseau) a la modeste ambition de répondre à différents objectifs :
- Être sans dépendance
- Être simple à utiliser
- Être généraliste et non rattaché à une architecture (notamment MV*)
- Ne pas contraindre la structure des objets à observer via l'héritage que ce soit par classe ou par interface
- Faire appel à des observateurs sous la forme de fonctions
De ces contraintes a résulté la bibliothèque Paon qui en est à sa version 0.2.2 et que vous pouvez récupérer sur mon compte GitHub ou via npm.
A noter que bien qu'étant rédigée en TypeScript, cette bibliothèque peut être utilisée par des applications JavaScript (cf. répertoire dist/).
Cette bibliothèque est en open source sous licence MIT.
En espérant que cette bibliothèque pourra vous être utile. N'hésitez pas à me faire part de vos retours d'expérience et à la partager autour de vous.
Ce qui suit est la traduction en français de sa description en anglais.
Paon
Un composant Observateur en TypeScript/JavaScript.
- Pas d'héritage requis.
- L'observable est juste un composant à l'intérieur d'un objet.
- Les observateurs sont juste des fonctions.
Installation
npm install paon
Pour compiler le source TypeScript en JavaScript, vous pourriez avoir besoin d'installer le compilateur TypeScript :
npm install -g typescript
Pour générer la version JavaScript minifiée lors du build, vous pourriez avoir besoin d'installer uglifyjs :
npm install -g uglifyjs
Build
Les fichiers résultants sont créés dans le répertoire dist/.
Build complet (compilation et minification):
npm run build
Simple compilation (pas de minification):
npm run compile
Utilisation
Toutes les constantes, interfaces, classes et fonctions sont accessibles au sein de l'espace de nommage Paon.
Exemple simple
Voici un exemple simple où nous ajoutons le composant observable à l'intérieur de notre classe Subject :
Code typescript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /// <reference path="paon.d.ts" /> class Subject { private name: string; observable: Paon.Observable; // Composant Observer Pattern constructor(name: string) { this.name = name; this.observable = new Paon.Observable(); // Instanciation / Initialisation } changeName(name: string): string { this.name = name; this.observable.notifyObservers("nameChanged"); // Un message est envoyé aux observateurs return this.name; } } function onNameChanged() { alert("Name has changed"); } let subject = new Subject("Penelope"); subject.observable.addObserver("nameChanged", onNameChanged); // La fonction onNameChanged() souscrit aux messages "nameChanged" du sujet subject.changeName("Melissa"); // Une popup d'alerte apparaît: "Name has changed" |
Ci-dessus, dans la classe Subject, la méthode changeName() enverra un message "nameChanged" aux observateurs de l'instance.
Après l'instanciation de Subject, la fonction onNameChanged() souscrit aux messages "nameChanged" du sujet.
Par conséquent, lorsque changeName() est appelée, une popup d'alerte apparaît.
Comme nous pouvons le voir, avec un tel pattern, aucun héritage via extends ou implements n'est requis. Juste une simple composition.
Exemple avec des données complémentaires
Nous pouvons envoyer des données complémentaires aux observateurs comme nous pouvons le voir ci-dessous :
Code typescript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /// <reference path="paon.d.ts" /> class Subject { private name: string; observable: Paon.Observable; // Composant Observer Pattern constructor(name: string) { this.name = name; this.observable = new Paon.Observable(); // Instanciation / Initialisation } changeName(name: string): string { this.name = name; this.observable.notifyObservers("nameChanged", { data: name }); // Un message avec une donnée complémentaire est envoyé aux observateurs return this.name; } } function onNameChanged(msg: { data: string }) { alert("Name has changed into " + msg.data); } let subject = new Subject("Penelope"); subject.observable.addObserver("nameChanged", onNameChanged); // La fonction onNameChanged() souscrit aux messages "nameChanged" du sujet subject.changeName("Melissa"); // Une popup d'alerte apparaît: "Name has changed into Melissa" |
Le paramètre msg dans la function onNameChanged() contient la donnée complémentaire que nous avons envoyé via la méthode changeName(). Ici, c'est un objet avec la propriété data, mais ça pourrait être n'importe quoi.
Importation de module
Cette bibliothèque peut également être importée en tant que module avec l'instruction import :
Code typescript : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import Paon from "./paon"; // Emplacement du fichier de déclaration .d.ts class Subject { private name: string; observable: Paon.Observable; // Composant Observer Pattern constructor(name: string) { this.name = name; this.observable = new Paon.Observable(); // Instanciation / Initialisation } changeName(name: string): string { this.name = name; this.observable.notifyObservers("nameChanged", { data: name }); // Un message avec une donnée complémentaire est envoyé aux observateurs return this.name; } } function onNameChanged(msg: { data: string }) { alert("Name has changed into " + msg.data); } let subject = new Subject("Penelope"); subject.observable.addObserver("nameChanged", onNameChanged); // La fonction onNameChanged() souscrit aux messages "nameChanged" du sujet subject.changeName("Melissa"); // Une popup d'alerte apparaît: "Name has changed into Melissa" |
Seule l'instruction import remplace la référence des exemples précédents. A part cela, le reste du code est identique.
Documentation de l'API
Ajoute un observateur à un type de message (similaire à la fonction DOM addEventListener()) :
Paon.Observable.addObserver(type: string, observer: Observer): void;
Retire un observateur d'un type de message (similaire à la fonction DOM removeEventListener()) :
Paon.Observable.removeObserver(type: string, observer: Observer): void;
Retire tous les observateurs d'un type de message :
Paon.Observable.removeObserversType(type: string): void;
Envoie une message aux observateurs (similaire à la fonction DOM dispatchEvent()) :
Paon.Observable.notifyObservers(type: string, msg?: any): void;
Contributeurs
yahiko
Licence
MIT
Vous avez lu gratuitement 681 articles depuis plus d'un an.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.