Patron de conception: L’observateur
Cette semaine, je vous présente le grand retour de la série sur les patrons de conception. Nous en verrons un bien connu que vous utilisez probablement chaque jour. Effectivement, la plupart des implémentations d’MVC utilisent un système d’observateur/observable pour gérer l’intéraction événementielle entre la vue et le contrôleur. Examinons-le plus en détails…
Les sources de cet article sont disponibles sur GitHub.
L’observateur (Observer Pattern)
Le problème
À part un script qui automatise une tâche, règle générale, une application nécessite une intéraction avec l’utilisateur. Cette dernière prend la forme d’événements déclanchés par l’action de l’utilisation avec un composant du système. On pense à un clic sur un bouton, à un changement de valeur dans un champ de formulaire ou encore au survol d’un élément avec la souris pour, par exemple, changer sa couleur.
De plus, certains événements pourraient nécessiter plusieur actions à prendre. Donc le principe, c’est d’avoir n éléments qui dépendent d’un seul autre, sans les coupler fortement.
La solution – l’observable
Dans cet exemple, nous allons créer une implémentation du patron en HTML et JavaScript. Le but étant d’avoir un champ texte observable et 3 champs de sortie synchronisés avec le contenu du champ texte.
1 2 3 4 5 6 |
<label for="userInput">Entrez du texte : </label> <input id="userInput" type="text"> <output id="liveOutput1"></output> <output id="liveOutput2"></output> <output id="liveOutput3"></output> |
- Surprise, output est une nouvelle balise qui est prise en charge par tous les navigateurs… sauf IE.
À titre de démo, nous allons utiliser jQuery et JavaScript pour créer 2 prototypes ou classes afin de synchroniser la valeur du champs avec les sorties. Pour commencer, créons notre première fonction qui servira d’observable. Cette fonction aura pour caractéristiques de posséder un état et de notifier x observateurs qui sont abonnés à l’observable.
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 |
function UserInputObservable (observedField) { let userInput = ''; const registeredObservers = []; initialize(); function initialize() { observedField.keyup(function () { userInput = $(this).val(); notifyObservers(); }); } function notifyObservers() { for (const currentObserver of registeredObservers) { currentObserver(userInput); } } function registerToInputChange(observer) { registeredObservers.push(observer); } return { registerToInputChange: registerToInputChange }; } |
Remarquez l’utilisation du module pattern afin de créer un prototype encapsulé. Ce pattern nous permet, entre autre, d’avoir une émulation du concept de membres privés. UserInputObservable offre une seule méthode public qui sert à enregistrer un observateur à l’observable. Cela signifie que, dès que l’état de l’observable change, les x observateurs seront notifiés pour se mettre à jour.
1 2 3 |
function registerToInputChange(observer) { registeredObservers.push(observer); } |
Une fois que nous avons une façons d’enregistrer des observateurs, il faut créer une méthode privée qui servira à avertir tous les observateurs enregistrés. Ici, puisqu’on est en JavaScript, nous pouvons considérer un observateur comme étant une fonction.
1 2 3 4 5 |
function notifyObservers() { for (const currentObserver of registeredObservers) { currentObserver(userInput); } } |
Puis, comme dernière étape en lien avec l’observable, il faut appeler la fonction privée lorsque son état change. Ici, c’est lorsque l’utilisateur relâche une touche du clavier dans le champs texte.
1 2 3 4 5 6 |
function initialize() { observedField.keyup(function () { userInput = $(this).val(); notifyObservers(); }); } |
Ce qui nous permet de créer l’observable comme ceci :
1 2 |
const userInputField = $('#userInput'); const obersvable = new UserInputObservable(userInputField); |
Et l’observateur…
Pour l’observateur, il nous faudra 2 paramètres pour cet exemple. Le premier étant le champ de sortie à synchroniser, le deuxième étant l’observable qui notifiera l’observateur en cas de changement d’état. Voici le code complet.
1 2 3 4 5 6 7 8 9 10 |
function UserOutputObserver (liveOutput, observable) { initialize(); function initialize() { observable.registerToInputChange(function (newValue) { liveOutput.html(newValue); }); } } |
Trop simple, non? À l’initialisation, nous appelons la méthode de l’observable passé en paramètre qui nous permet de s’abonner à ce dernier. En paramètre à cet appel de méthode, nous lui passons ladite fonction qui sera exécutée lorsque l’état de l’observable change.
Conclusion
Aujourd’hui, vous avez appris à créer une implémentation simple du patron de conception observateur/observable. Ce patron est utilisé à différentes sauces dans les frameworks MVC et MVVM tels qu’Angular ou MVC.Net. Pour pousser le concept encore plus loins, essayez de réutiliser ce patron dans un cadre ou l’interface graphique s’abonnerait directement avec le backend pour avoir, par exemple, une application en temps réel du style Google Docs.
De plus, vous avez utilisé 3 concepts simples de programmation OO : le patron Observateur/Observable, le module pattern et l’injection de dépendances. En effet, nous avons vu que le problème initial était lié au fait qu’on voulait découpler les observateurs et l’observable, ce qui a été résolu avec une implémentation de l’injection de dépendances par constructeur. Consulter notre article sur les principes SOLID pour plus de détails.
Merci de nous lire chaque semaine et de partager cet article sur vos plateformes sociales favorites 🙂 N’hésitez surtout pas à nous suivre sur Facebook.
Code source
Les sources de cet article sont disponibles sur GitHub.
1 |
git clone http://github.com/EzoQC/ObserverPattern.git |
Commentaires
Laisser un commentaire