Bonjour. Alors dans cette séquence, on va voir quelque
chose d'extrêmement intéressant et important en Pharo,
qui est le does not understand. Donc en fait j'imagine
que vous avez déjà eu des débuggers qui sont ouverts avec
la méthode doesNotUnderstand affichée.
Donc on va voir d'où elle vient et à quoi elle sert.
Donc on va prendre un exemple, ici.
Donc je vais vous l'expliquer.
On a un objet ici, node1.
Cet objet on va lui envoyer le message Coucou Stef.
Donc ici, c'est le symbole Steph.
Donc node1, si vous reprenez le lookup, on va remonter à
la classe de cet objet.
Est-ce qu'il existe une méthode coucou sur cette classe?
Non. Alors on va remonter à la super classe.
Est-ce qu'il existe une méthode coucou dans la face object?
Non. Alors que va faire la machine virtuelle?
Elle va dire: je n'ai pas trouvé de méthode
correspondant à l'envoi de messages effectués sur l'objet
donc je fais réifier le message.
Donc réifier, j'en ai déjà parlé dans la séquence sur l'introspection,
sur la réflexion. Donc l'idée de réifier, c'est de
représenter sous la forme d'un objet, un concept qui
normalement est implicite, donc en l'occurrence ici le message.
Donc ici, on va créer un objet pour représenter le message Coucou Stef.
Donc c'est une instance de la classe message en Pharo.
Et puis la machine virtuelle, ce qu'elle va faire, c'est
qu'elle va réenvoyer, elle va refaire un envoi de message
à cet objet node1 ici, sauf qu'elle va lui envoyer le
message doesNotUnderstand, et lui passer cet objet message en paramètre.
Donc il y a une nouvelle exécution de l'algorithme de lookup.
Cette fois, la machine virtuelle va chercher est-ce qu'il
existe la méthode doesNotUnderstand dans la classe Node?
Non. Alors elle remonte.
Donc on remonte dans la super classe.
Est-ce qu'il existe une méthode doesNotUnderstand dans la classe objet?
Oui, elle est là. Donc là, elle est sous forme abrégée
dnu, mais c'est la méthode doesNotUnderstand, donc elle
va pouvoir être exécutée.
Donc ce qu'il faut bien comprendre, c'est que
doesNotUnderstand c'est un message qu'on va envoyer aux
objets, que la machine virtuelle va envoyer aux objets
pour vous, quand un envoi de message a échoué.
Donc toutes les classes peuvent redéfinir cette méthode
doesNotUnderstand, pour vraiment avoir son propre
comportement quand un message n'est pas compris par l'une de ces instances.
Donc c'est vraiment important, ça permet de construire
énormément de choses grâce à cette méthode
doesNotUnderstand, grâce à ce hook. Donc ça permet de
construire de la délégation automatique, c'est utilisé
aussi dans la programmation distribuée et caetera.
Donc nous, on va voir quelques usages possibles du
doesNotUnderstand dans cette séquence.
Donc l'idée, par exemple, imaginons que je veuille
rediriger tous les messages reçus par un objet vers un autre.
Donc la délégation simple.
Donc pour ça, je vais construire un objet Delegating,
donc qui va stocker la cible, vers quel objet je dois
renvoyer tous les messages. Je vais redéfinir la méthode doesNotUnderstand.
Je vous l'ai dit, en paramètres elle prend un objet
message qui va contenir le sélecteur du message qui n'avait
pas été compris, ainsi que la liste des arguments.
Et puis je peux demander à ce message de se rediriger, d'être
réenvoyé, donc je vais utiliser sendTo self target, qui
est bien la variable d'instance.
Donc je vais réenvoyer ce message à un autre objet.
Donc attention, cette pratique c'est extrêmement
puissant, mais également ça nuit un petit peu à la
lisibilité du code.
Vous l'avez ici. Il faut vraiment aller lire le code pour
savoir qui est-ce qui va recevoir le message in fine.
Par contre, c'est extrêmement pratique pour la
construction d'outils ou la construction de mécanismes un
peu avancés, comme je vous l'ai dit précédemment.
Donc on va voir un autre exemple ensemble, le loggingProxy.
L'idée de base c'est de créer un objet minimal qui va
contenir très peu de méthode, de customiser, de redéfinir
sa méthode doesNotUnderstand. Et puis ensuite, ce qu'on
va faire, c'est qu'on va échanger un objet métier grâce à
become avec cet objet minimal, ce proxy.
Donc voilà comment on va faire, je crée un objet proxy.
Donc cet objet proxy, je vais mettre un sujet.
Donc le sujet, c'est l'objet que je vais remplacer, donc
la cible, la target.
Et puis je vais, par exemple, mettre un Counter d'invocation.
Donc à chaque fois qu'un message va être reçu, on va incrémenter le Counter.
Dans initialize, je vais mettre à 0.
Le Counter d'invocation, pour l'instant, n'a reçu aucun
message et puis le sujet qui est-ce qu'on va remplacer.
Maintenant on va redéfinir la méthode doesNotUnderstand
sur ce proxy, donc on va dire à chaque fois qu'il reçoit
un message qu'il ne comprend pas il va afficher sur le
transcript qu'il a reçu un message.
Il va incrémenter son counter, j'ai reçu un nouveau
message, un de plus et puis je vais transférer le message
à mon sujet. Donc comme précédemment, je transfère le
message à quelqu'un d'autre.
Donc pour le sendTo, si vous regardez l'implémentation
dans la classe Message c'est tout bête, on avait déjà vu,
c'est la méthode perform withArguments.
On en avait déjà parlé dans une séquence précédente, donc
là il n'y a rien de nouveau.
Voyons maintenant un exemple, comment est-ce qu'on peut
utiliser ce loggingProxy.
Donc on va créer une instance, un objet point, et puis je
vais utiliser maintenant become pour dire: tous ceux qui
pointent sur ce point, ils vont pointer sans s'en rendre
compte sur un loggingProxy, que j'ai instancié.
Et maintenant, si j'envoie des messages à l'objet point,
donc attention on intègre maintenant l'objet point vu qu'il
y a eu become, c'est devenu l'instance du loggingProxy.
Donc à chaque fois que je lui envoie un message,
forcément ça va s'afficher sur le transcript.
Et puis le Counter d'invocations va être incrémenté.
Vous vous souvenez, dans le doesNotUnderstand c'est ce qu'on faisait.
Donc on peut voir qu'à la fin, le Counter invocations, il vaut bien 3.
Donc il y a quelques limites, par exemple dans le petit
framework de proxy, exemple que je viens de présenter, il
y a des limites, on ne peut pas forcément capturer par
exemple les messages qu'un objet s'envoie à lui-même, ça va être difficile.
On ne peut pas utiliser un become sur des classes, donc
les opérations réflexives ont des limites en Pharo.
Par exemple, ce qui va être compliqué aussi, imaginons
que l'objet à remplacer et le proxy comprennent tous les 2 le même message.
Ce qui fait que quand je vais envoyer le message au
proxy, il va répondre à la place de le capturer avec un
doesNotUnderstand, et de le transférer à son objet cible.
Donc, il y a d'autres frameworks de proxy, plus puissants
qui existent en Pharo, qui permettent de vraiment tout
capturer, mais ils sont plus importants qu'un exemple
aussi petit que celui-là.
Donc une autre application, un autre exemple d'utilisation
de cette technique, ça peut être par exemple la
génération des accesseurs ou même des méthodes
dynamiquement. Par exemple, imaginons ici toujours
définie la méthode doesNotUnderstand et puis je vais dire
: OK, il y a un message qui a été reçu donc je vais
tester est-ce que j'ai des variables d'instances qui incluent ce message?
Si oui, alors je vais générer une nouvelle méthode donc
je vais utiliser compile pour générer une nouvelle
méthode sur la classe, qui va répondre
directement la valeur de cette variable.
Donc je suis en train de générer l'accesseur en lecture d'une variable.
Et puis si vraiment si le message qui a été envoyé ne
correspond pas à un nom d'une variable d'instances, je
fais un super doesNotUnderstand. Donc ça va nous
permettre d'automatiquement remplir les accesseurs en
lecture, en fonction du fait qu'ils ont été appelés ou pas sur un objet.
Donc en conclusion dans cette séquence, on peut voir qu'on
va être capables d'utiliser des objets minimaux.
Donc on a vu que ce n'était pas des objets qui étaient
directement instance de la classe Object mais de la
classe proto object, donc c'est vraiment des tout petits objets.
Ça peut être la base pour des frameworks de proxy.
Grâce à la redéfinition de la méthode doesNotUnderstand,
on va être capable de capturer le fait qu'il y a un envoi
de message qui a raté, et on va pouvoir avoir un
comportement, rediriger ce message ailleurs et avoir un
traitement spécifique.
Donc c'est un hook qui est extrêmement puissant, qui est
à la base de beaucoup d'outils en Pharo.
Et par contre, il faut faire attention, il faut vraiment
l'utiliser que quand c'est réellement nécessaire, quand
vraiment on en a un besoin. Donc ne mettez pas ça dans du
code métier, encore ça reste une technique avancée de Pharo.