Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Paon : le design pattern Observateur sans héritage en TypeScript

Le , par yahiko

0PARTAGES

Paon : le design pattern Observateur sans héritage en TypeScript / JavaScript


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

Une erreur dans cette actualité ? Signalez-le nous !