Balade au pays des voyeurs : les Observables

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.

They posted on the same topic

Trackback URL : https://www.cynicalturtle.net/kame/trackback/2086

This post's comments feed