Tout commence par la découverte du code d'un programme utilisant Angular, et en particulier NgRx. Je me retrouve propulsé plus de 20 ans en arrière, avec une approche intégralement en callbacks, et pour s'y retrouver dans ce magma de fonctions : des noms, des noms, des noms. Une explosion de noms. Une addition ? Un nom. L'affectation à une variable ? Un nom. Cette pratique antédiluvienne de nommer tous ces petits fragments de code avait normalement disparue quand on a arrêté de faire de l'assembleur. Et pourtant, NgRx est à la mode. Et ça parce que NgRx est au pinacle de la modernité, un croisement entre Redux, la programmation réactive
et Angular : en cumulant trois biens, comment peut-on être autrement qu'exceptionnel ?
Revenons sur ces noms : on parle de magasin, d'action, de réducteur, d'observable et d'effet, avec des liaisons sémantiques souvent parachutées. La lecture de la documentation est surprenante, on la croirait écrite par un ChatTouPT : la grammaire est correcte, les mots existent, des phrases font sens, et tout d'un coup, le vide, l'absence de logique, l'absence de sens... Au milieu de cette forêt je repère le mot observable
, répété régulièrement par des collègues, qui me rappelle naïvement le modèle de conception observateur
du GoF. J'aborde donc le sujet avec dans l'idée qu'un observable
est celui qui est observé par un observateur
(ou observer). Mais ça ne fonctionne pas. On pourrait dire que les effets et les réducteurs sont des sortes d'observateurs
, mais un observateur
est normalement notifié selon une interface qui a du sens pour la quantité de données et les changements poussés par l'observé. Ici rien de tout ça, l'observateur
doit avant tout faire attention à... la gestion des erreurs d'une part, et à la notion de complétion d'autre part.
Côté GoF, la gestion des erreurs ne fait pas partie de Observateur
, elle est réalisée à travers les mécanismes standards en POO, c'est-à-dire avec des exceptions. Et pour la notion de complétion, celle-ci se matérialise par la mise en place d'un gestionnaire de changement ou ChangeManager si cette notion est importante à gérer, ce qui n'est pas le cas de base de Observateur
. D'autres choses font tiquer, comme le fait de créer un observable
avec une simple valeur, qu'on change ensuite à la main, sans aucune notion d'extériorité. On trouve aussi dans la documentation un observable
qui ne change pas, et donc qui n'a pas besoin d'être observé, vu que le patron de conception Observateur
n'est là que pour ça : le changement.
Quelque chose n'allait donc pas dans la compréhension de NgRx par l'angle du patron de conception Observateur
. Je reprends mes pérégrinations, je retombe sur la documentation officielle de NgRx, et tout me paraît soudainement moins confus, plus explicite, et même cohérent. Ça parle beaucoup moins de ces actions
qui ne suivent pas le patron de conception Commande
du GoF mais qui sont juste des encapsulations de paramètres avec un nom. Ça ne parle pas plus non plus de ces fameux réducteurs
, qui dans Commande
sont la partie active de l'action
. Jusqu'à ce que quelqu'un me signale que ce n'était pas la documentation de NgRx mais de RxJS !
Sur cette documentation on apprend d'autres choses, comme par exemple qu'il est logique de recevoir des événements alors que la souscription n'est pas terminée, voire qu'il est normal qu'il n'y ait plus rien à observer une fois la souscription faite, tout se passant pendant la souscription. Là on est à l'opposé complet d'Observateur
, où on a quasiment la garantie que rien ne va se passer pendant la souscription. En continuant un peu dans la partie glossaire, on va découvrir l'observable froid et l'observable chaud. L'observable froid, c'est celui qui crée un producteur pour chaque souscripteur au moment de la souscription. Et là nous sommes passés complètement au-delà du patron de conception Observateur
, dans lequel on n'a pas besoin de notions séparées de producteur et de consommateur. Le sujet
produit, l'observateur
... ne consomme pas : en effet, quand quelque chose est consommé, il n'est plus disponible pour quelqu'un d'autre, et dans le patron de conception Observateur
, tous les observateurs
reçoivent une notification à la base identique.
Alors pourquoi introduire cette notion de consommateur, c'est étrange ! Continuons sur le glossaire RxJS, avec l'observable
qui est défini comme un gabarit pour connecter un observateur
en tant que consommateur
à un producteur
via une action souscrire
, résultant en une souscription
. RxJS définit donc que l'observable
n'est pas observé, c'est le producteur
qui l'est, parce que l'observateur
n'est rien de plus qu'un consommateur
. On ne trouve pas dans ce même glossaire la notion de sujet
, elle n'est pas suffisamment importante, mais elle est quand même présente dans la documentation. Étant donné que le mot est directement copié depuis le patron de conception Observateur
, dont RxJS se réclame en tant que... ReactiveX ? Diantre, encore un nouveau terme. Bref, étant donné que RxJS se réclame de Observateur
, alors on devrait trouver une très forte similarité sous l'intitulé sujet
.
RxJS présente sujet
comme ceci : un sujet est un type spécial d'observable qui permet aux valeurs d'être envoyées à plusieurs observateurs
. Deux remarques : dans Observateur
, tous les observateurs reçoivent la même notification, néanmoins, une notification dans Observer
n'est de base pas sous la forme d'une valeur, mais d'un signal. Un observable
standard n'est donc définitivement par un sujet
dans Observer
, il n'en a pas du tout les mêmes caractéristiques. RxJS met aussi en avant la caractéristique suivante : tout sujet est un observateur ; c'est un objet possédant les méthodes de l'observateur
. A ce stade, il est temps d'aller ouvrir les fenêtres, taper deux ou trois tapis, et passer un coup d'aspirateur. Cette récursivité n'a aucune existence dans le patron de conception Observateur
: il n'y a aucune raison pour le sujet
d'être un observateur
, absolument aucune. Parce que ce patron de conception suit un des principes de bases de l'informatique : la séparation des préoccupations. Juge et partie. Créancier et endetté. Surveillant et prisonnier. Sujet
et observateur
.
Puisque ce mot ReactiveX est apparu au-dessus, allons voir si cette notion est différente de ce côté-ci. La présentation commence comme suit : un Sujet est une sorte de pont ou de mandataire disponible dans certaines implémentations de ReactiveX, qui agit à la fois comme observateur et comme Observable
. La finalité est inversée par rapport à RxJS. Dans un cas on dit que le principe est de pouvoir être utilisé comme Sujet
au sens de Observateur
(sans le désigner), de l'autre on se décorrèle de Observateur
et on le définit directement par un autre patron de conception, uniquement sous un enjeu technique. Et dans les deux documentations, il est très clair qu'aucun pont direct avec le patron de conception Observateur
n'est écrit. En tout cas, la page Sujet
côté ReactiveX est abondamment alimentée à l'aide de diagrammes, dits diagrammes de billes, parfaits pour illustrer la stratégie d'architecture tuyaux et filtres
(pipes and filters), décrit dans Pattern-Oriented Software Architecture (plus connu sous le petit nom de POSA).
Du côté de notre documentaliste préféré de patrons en tout genre, oui, je parle de Martin Fowler, l'approche de ReactiveX est catégorisée comme patron de programmation, sous le nom pipeline pour les collections
(Collection Pipeline), qu'il définit comme étant une spécialisation de tuyaux et filtres
. Bien sûr, aucun de ces termes n'est référencé dans la documentation de ReactiveX. Au final, cette partie Observateur, mise en avant extrêmement fortement par ReactiveX et ses dérivés, ne participe pas vraiment à la compréhension de ce qu'est ReactiveX et Observable. C'est à la limite dommageable pour ceux qui connaissent bien ce patron de conception, les emmenant sur des fausses pistes logiques.
Une note humoristique pour conclure : le terme observable
avait été utilisé dans InterViews de Mark A. Linton et al. Un des des co-auteurs du GoF avait travaillé sur InterViews, le GoF étant un travail postérieur. L'observable
de InterViews est identique au sujet
du GoF. C'est comique non ?
Petite anecdote : côté Java, depuis plus d'un quart de siècle, la classe représentant le Sujet
s'appelle java.util.Observable
.
Vous pouvez trouver le reste de la série sous l'article d'introduction.