Hello, this video will explain an important feature of Pharo,
the doesNotUnderstand hook.
You've no doubt already seen a debugger window with this message.
Where does it come from and what is it for?
Let's look at an example.
We have the object node1.
We'll send this object the message coucou: stef.
From node1, we follow the lookup method
back to the object's class.
We don't find the coucou method in this class.
So, we go to the superclass, but it's not there either.
The virtual system informs us
that it can't find a matching method, so it reifies the message.
We discussed reification in the video on introspection and reflection.
Reify means to represent an implicit concept as an object.
Here, it's a message.
We create an object to represent the message.
It's an instance of Message class.
The virtual system will resend a message
to the object node1.
It sends the doesNotUnderstand message, passing the object as a parameter.
This is a new execution of the lookup algorithm.
The system will not find the doesNotUnderstand method in Node class.
So, it goes back to search in the superclass.
It finds the method, abbreviated to DNU.
Now it can now execute the method.
DoesNotUnderstand is a message
that the virtual machine sends for you to objects
when a message has failed.
All classes can redefine this method
to give it a specific behavior when a message is not understood.
This method is an important tool
that allows us to build many features.
It's used in automatic delegation,
distributed programming, and so on.
Here we'll look at some uses of doesNotUnderstand.
Suppose we want to redirect all our messages to a different object.
In a simple delegation, I create an object that stores the target,
where messages will be sent.
I redefine the doesNotUnderstand method.
It takes the object aMessage as a parameter,
containing the failed message selector.
Then I can ask this message to resend itself.
I use sendTo: self target, which is the instance variable.
I resend the message to another object.
Be careful, this is a powerful function
that can interfere with the legibility of the code.
As it's explained here.
The code will show who ultimately receives the message.
It's very useful for constructing tools
and building advanced mechanisms.
Another example we'll look at is LoggingProxy.
The basic idea here
is to create a minimal object that contains few methods,
and to customize its doesNotUnderstand: method.
Then we'll swap a domain object
with this proxy, or minimal object, using "become."
First, we create a proxy object.
We give the proxy object a subject,
which is the object we're going to replace,
or the target.
We add InvocationCount to increment the counter with each message.
We put the counter in initialize at 0, since it has received no messages.
Then we add the subject, which is what will be replaced.
Now we redefine the DNU method on this proxy.
Every time a new message is not understood,
its reception appears on the Transcript, and the counter is incremented.
Then we forward the message to the subject.
Like before, we redirect the message elsewhere.
For sendTo, as you see in the Message class implementation,
we simply use the Perform method with Arguments.
We already explained this in an earlier course.
Let's look at an example.
How do we use this LoggingProxy?
We'll create an instance, the Object point.
Then we'll use "become"
so that everything that points to this point
will inadvertently point to a LoggingProxy,
which we instantiate.
Now if we send messages to the Object point,
noting that "become" is now an instance of LoggingProxy,
every time we send it a message,
it will display in Transcript
and the counter will be incremented.
This is what we did in doesNotUnderstand.
At the end, the counter shows a value of 3.
There are limits to the proxy's framework,
as seen in the last example.
For example, we can't capture messages an object sends to itself.
That gets tricky.
We can't use "become" with classes, as reflective models have limitations.
It's also risky when the proxy and the object to be replaced
both understand the same message.
When I send the message to the proxy,
it will reply, rather than capturing it with DNU
and transferring it to the target object.
Pharo has other more powerful proxy frameworks that trap everything,
but they're more complex than this simple example.
Another example of how to apply this technique
is to dynamically generate accessors, or even methods.
Here I've redefined the doesNotUnderstand method.
I see a message has been received,
so I test if I have the instance variables that include the message.
If so, I generate a new method using "compile,"
to define a new method for the class.
This will return the value of the variable.
Here I'm generating the variable's read-accessor.
If the message sent does not match the name
of an instance variable, I send a super doesNotUnderstand.
This enables us to load read-accessors automatically,
depending on if they've been called or not.
In conclusion,
we've seen how to use minimal objects.
They're not direct instances of the Object class,
but of the proto Object class.
They can be the basis for proxies.
By redefining the doesNotUnderstand method, we can capture
the existence of a failed message and then redirect the message.
It's a powerful hook that provides the basis of many tools in Pharo.
But you must be very careful when you use it.
It's only to be used when necessary.
Don't put it in the domain code yet, it's a highly advanced technique.