1 00:00:00,720 --> 00:00:05,880 Hello, this video will explain an important feature of Pharo, 2 00:00:06,080 --> 00:00:07,920 the doesNotUnderstand hook. 3 00:00:08,080 --> 00:00:13,400 You've no doubt already seen a debugger window with this message. 4 00:00:13,920 --> 00:00:16,840 Where does it come from and what is it for? 5 00:00:17,280 --> 00:00:20,080 Let's look at an example. 6 00:00:20,400 --> 00:00:22,280 We have the object node1. 7 00:00:22,640 --> 00:00:27,520 We'll send this object the message coucou: stef. 8 00:00:28,280 --> 00:00:31,280 From node1, we follow the lookup method 9 00:00:31,840 --> 00:00:33,920 back to the object's class. 10 00:00:34,200 --> 00:00:37,720 We don't find the coucou method in this class. 11 00:00:37,920 --> 00:00:42,320 So, we go to the superclass, but it's not there either. 12 00:00:42,720 --> 00:00:45,520 The virtual system informs us 13 00:00:45,800 --> 00:00:51,040 that it can't find a matching method, so it reifies the message. 14 00:00:51,200 --> 00:00:56,440 We discussed reification in the video on introspection and reflection. 15 00:00:56,920 --> 00:01:02,640 Reify means to represent an implicit concept as an object. 16 00:01:02,920 --> 00:01:04,520 Here, it's a message. 17 00:01:04,920 --> 00:01:07,920 We create an object to represent the message. 18 00:01:08,080 --> 00:01:10,400 It's an instance of Message class. 19 00:01:10,680 --> 00:01:15,000 The virtual system will resend a message 20 00:01:15,200 --> 00:01:17,320 to the object node1. 21 00:01:17,760 --> 00:01:22,800 It sends the doesNotUnderstand message, passing the object as a parameter. 22 00:01:22,920 --> 00:01:27,000 This is a new execution of the lookup algorithm. 23 00:01:27,200 --> 00:01:32,440 The system will not find the doesNotUnderstand method in Node class. 24 00:01:32,720 --> 00:01:37,640 So, it goes back to search in the superclass. 25 00:01:37,840 --> 00:01:40,920 It finds the method, abbreviated to DNU. 26 00:01:41,160 --> 00:01:44,440 Now it can now execute the method. 27 00:01:47,080 --> 00:01:49,920 DoesNotUnderstand is a message 28 00:01:50,200 --> 00:01:53,840 that the virtual machine sends for you to objects 29 00:01:54,280 --> 00:01:56,280 when a message has failed. 30 00:01:57,080 --> 00:02:00,680 All classes can redefine this method 31 00:02:01,280 --> 00:02:06,800 to give it a specific behavior when a message is not understood. 32 00:02:07,320 --> 00:02:09,840 This method is an important tool 33 00:02:10,320 --> 00:02:13,800 that allows us to build many features. 34 00:02:14,080 --> 00:02:17,440 It's used in automatic delegation, 35 00:02:17,720 --> 00:02:20,520 distributed programming, and so on. 36 00:02:21,400 --> 00:02:25,080 Here we'll look at some uses of doesNotUnderstand. 37 00:02:25,720 --> 00:02:30,400 Suppose we want to redirect all our messages to a different object. 38 00:02:31,640 --> 00:02:37,200 In a simple delegation, I create an object that stores the target, 39 00:02:37,400 --> 00:02:39,320 where messages will be sent. 40 00:02:39,520 --> 00:02:42,200 I redefine the doesNotUnderstand method. 41 00:02:42,400 --> 00:02:45,560 It takes the object aMessage as a parameter, 42 00:02:45,760 --> 00:02:49,280 containing the failed message selector. 43 00:02:49,680 --> 00:02:53,400 Then I can ask this message to resend itself. 44 00:02:53,760 --> 00:02:59,920 I use sendTo: self target, which is the instance variable. 45 00:03:00,520 --> 00:03:03,320 I resend the message to another object. 46 00:03:04,320 --> 00:03:07,400 Be careful, this is a powerful function 47 00:03:07,920 --> 00:03:12,400 that can interfere with the legibility of the code. 48 00:03:12,760 --> 00:03:15,160 As it's explained here. 49 00:03:16,000 --> 00:03:20,360 The code will show who ultimately receives the message. 50 00:03:20,920 --> 00:03:23,680 It's very useful for constructing tools 51 00:03:24,080 --> 00:03:28,080 and building advanced mechanisms. 52 00:03:29,720 --> 00:03:33,720 Another example we'll look at is LoggingProxy. 53 00:03:33,920 --> 00:03:36,320 The basic idea here 54 00:03:36,560 --> 00:03:40,400 is to create a minimal object that contains few methods, 55 00:03:40,600 --> 00:03:44,360 and to customize its doesNotUnderstand: method. 56 00:03:44,800 --> 00:03:48,840 Then we'll swap a domain object 57 00:03:49,400 --> 00:03:53,920 with this proxy, or minimal object, using "become." 58 00:03:54,840 --> 00:03:58,840 First, we create a proxy object. 59 00:03:59,360 --> 00:04:02,840 We give the proxy object a subject, 60 00:04:03,080 --> 00:04:06,560 which is the object we're going to replace, 61 00:04:07,400 --> 00:04:08,560 or the target. 62 00:04:08,840 --> 00:04:14,720 We add InvocationCount to increment the counter with each message. 63 00:04:15,400 --> 00:04:20,760 We put the counter in initialize at 0, since it has received no messages. 64 00:04:20,920 --> 00:04:25,400 Then we add the subject, which is what will be replaced. 65 00:04:26,520 --> 00:04:29,760 Now we redefine the DNU method on this proxy. 66 00:04:29,920 --> 00:04:33,360 Every time a new message is not understood, 67 00:04:33,560 --> 00:04:38,840 its reception appears on the Transcript, and the counter is incremented. 68 00:04:39,200 --> 00:04:43,400 Then we forward the message to the subject. 69 00:04:44,080 --> 00:04:47,600 Like before, we redirect the message elsewhere. 70 00:04:48,800 --> 00:04:52,920 For sendTo, as you see in the Message class implementation, 71 00:04:53,080 --> 00:04:57,400 we simply use the Perform method with Arguments. 72 00:04:57,640 --> 00:05:01,440 We already explained this in an earlier course. 73 00:05:02,920 --> 00:05:05,160 Let's look at an example. 74 00:05:06,200 --> 00:05:09,520 How do we use this LoggingProxy? 75 00:05:10,000 --> 00:05:13,280 We'll create an instance, the Object point. 76 00:05:14,040 --> 00:05:16,680 Then we'll use "become" 77 00:05:17,040 --> 00:05:20,200 so that everything that points to this point 78 00:05:20,680 --> 00:05:24,280 will inadvertently point to a LoggingProxy, 79 00:05:25,520 --> 00:05:27,320 which we instantiate. 80 00:05:27,760 --> 00:05:30,760 Now if we send messages to the Object point, 81 00:05:30,920 --> 00:05:36,600 noting that "become" is now an instance of LoggingProxy, 82 00:05:36,840 --> 00:05:39,200 every time we send it a message, 83 00:05:39,840 --> 00:05:42,000 it will display in Transcript 84 00:05:42,720 --> 00:05:45,320 and the counter will be incremented. 85 00:05:46,000 --> 00:05:48,800 This is what we did in doesNotUnderstand. 86 00:05:49,040 --> 00:05:53,200 At the end, the counter shows a value of 3. 87 00:05:54,440 --> 00:05:57,840 There are limits to the proxy's framework, 88 00:05:58,000 --> 00:06:00,200 as seen in the last example. 89 00:06:00,520 --> 00:06:06,440 For example, we can't capture messages an object sends to itself. 90 00:06:06,640 --> 00:06:07,840 That gets tricky. 91 00:06:08,040 --> 00:06:14,160 We can't use "become" with classes, as reflective models have limitations. 92 00:06:14,720 --> 00:06:18,720 It's also risky when the proxy and the object to be replaced 93 00:06:18,920 --> 00:06:21,400 both understand the same message. 94 00:06:21,760 --> 00:06:24,600 When I send the message to the proxy, 95 00:06:24,840 --> 00:06:28,360 it will reply, rather than capturing it with DNU 96 00:06:28,560 --> 00:06:31,520 and transferring it to the target object. 97 00:06:32,280 --> 00:06:37,000 Pharo has other more powerful proxy frameworks that trap everything, 98 00:06:37,160 --> 00:06:42,200 but they're more complex than this simple example. 99 00:06:42,800 --> 00:06:46,760 Another example of how to apply this technique 100 00:06:47,080 --> 00:06:53,280 is to dynamically generate accessors, or even methods. 101 00:06:53,840 --> 00:06:58,400 Here I've redefined the doesNotUnderstand method. 102 00:06:58,800 --> 00:07:01,200 I see a message has been received, 103 00:07:01,400 --> 00:07:06,200 so I test if I have the instance variables that include the message. 104 00:07:06,400 --> 00:07:11,200 If so, I generate a new method using "compile," 105 00:07:11,560 --> 00:07:14,040 to define a new method for the class. 106 00:07:16,360 --> 00:07:19,600 This will return the value of the variable. 107 00:07:19,760 --> 00:07:23,600 Here I'm generating the variable's read-accessor. 108 00:07:24,080 --> 00:07:28,560 If the message sent does not match the name 109 00:07:28,920 --> 00:07:32,840 of an instance variable, I send a super doesNotUnderstand. 110 00:07:33,400 --> 00:07:38,400 This enables us to load read-accessors automatically, 111 00:07:38,920 --> 00:07:41,840 depending on if they've been called or not. 112 00:07:42,760 --> 00:07:45,280 In conclusion, 113 00:07:45,400 --> 00:07:47,920 we've seen how to use minimal objects. 114 00:07:48,080 --> 00:07:51,640 They're not direct instances of the Object class, 115 00:07:51,840 --> 00:07:54,040 but of the proto Object class. 116 00:07:54,200 --> 00:07:56,920 They can be the basis for proxies. 117 00:07:57,200 --> 00:08:01,400 By redefining the doesNotUnderstand method, we can capture 118 00:08:01,920 --> 00:08:07,800 the existence of a failed message and then redirect the message. 119 00:08:08,320 --> 00:08:13,840 It's a powerful hook that provides the basis of many tools in Pharo. 120 00:08:14,560 --> 00:08:18,000 But you must be very careful when you use it. 121 00:08:18,280 --> 00:08:21,760 It's only to be used when necessary. 122 00:08:22,080 --> 00:08:27,320 Don't put it in the domain code yet, it's a highly advanced technique.