Aide-mémoire, Head First Design Patterns, chapitre 2
Observer
Un objet prévient tous ses abonnés quand son état change. Il ne connaît d'eux que le contrat, et chacun peut s'abonner ou se désabonner pendant l'exécution.
« Définir une dépendance un-à-plusieurs entre des objets, de sorte que, quand un objet change d'état, tous ceux qui en dépendent soient prévenus et mis à jour automatiquement. » Gamma, Helm, Johnson et Vlissides, Design Patterns, 1994
Les trois rôles
- Le sujet,
Panier - Il garde la liste de ses observateurs. Après chaque changement, dans
ajoutercomme dansvider, il appelle#notifier, qui envoie l'état à chacun. - Le contrat,
ObservateurPanier - Une seule méthode,
panierModifie(etat), que le sujet appelle sur chaque abonné. - Les observateurs,
Badge,Recap,Sauvegarde - Chacun réagit à sa façon. Aucun ne connaît les autres.
Le code qui assemble l'application branche les abonnés de l'extérieur avec panier.abonner(badge). Il peut aussi en retirer un avec panier.desabonner(badge).
Le livre compare le sujet à un éditeur de journal, et les observateurs à ses abonnés.
Le reconnaître
Un changement doit déclencher plusieurs réactions sans rapport entre elles, et leur liste varie. Le signal le plus courant est une méthode qui appelle à la suite des objets étrangers à son travail. Dans le panier de départ, ajouter mettait à jour le badge, le récapitulatif et la barre de livraison offerte.
Avec un seul destinataire qui ne change pas, un appel direct reste plus simple à suivre.
Push ou pull
Le panier de l'exercice pousse (push) une photo de son état, EtatPanier, qui contient le nombre d'articles, le total et les lignes. Chaque observateur y lit ce qui l'intéresse. Un observateur qui aurait besoin d'une autre donnée obligerait à enrichir cette photo.
En pull, le sujet envoie sa propre référence, panierModifie(panier), et chaque observateur appelle les méthodes dont il a besoin. L'observateur dépend alors des méthodes publiques du sujet.
Le principe du chapitre
« Efforcez-vous de coupler faiblement les objets qui interagissent. » Panier connaît ObservateurPanier et ignore Badge. On ajoute, retire ou remplace un observateur sans modifier une ligne du panier.
Les principes du chapitre 1 reviennent aussi. Le panier programme pour une interface, et il compose une liste d'observateurs.
Classe ou fonction
Quand le contrat tient en une méthode, l'observateur peut être une simple fonction. abonner renvoie alors la fonction qui désabonne, et l'appelant n'a rien d'autre à garder : const arreter = panier.abonner(majBadge), puis arreter().
Dans le code web
addEventListener abonne une fonction aux événements d'un élément du DOM, qui joue le rôle du sujet. removeEventListener ou un AbortController la désabonnent.
Les stores de Svelte et de Redux exposent subscribe, qui renvoie la fonction de désabonnement. L'EventEmitter de Node applique le même pattern avec on, off et emit. Les signaux d'Angular et de Svelte 5 créent ces abonnements tout seuls quand un effet lit une valeur.
Le piège
Un observateur oublié reste en mémoire et continue de réagir. Un composant qui s'abonne doit donc se désabonner quand il disparaît, par exemple dans onDestroy, dans la fonction renvoyée par $effect ou avec controller.abort().
À vous
Strategy et Observer ont tous les deux une interface et plusieurs classes qui l'implémentent. Qu'est-ce qui les distingue ?
Voir la réponse
Le client de Strategy reçoit une seule variante, choisie de l'extérieur, et lui confie un calcul dont il attend le résultat. Le sujet d'Observer prévient tous ses abonnés après un changement, sans rien attendre en retour.