1 00:00:00,550 --> 00:00:02,930 Bonjour. Dans cette séquence, on va voir quelque chose d'un 2 00:00:03,130 --> 00:00:05,760 petit peu avancé en Pharo que sont les opérations 3 00:00:05,960 --> 00:00:08,190 réflexives qui permettent de supporter le live 4 00:00:08,390 --> 00:00:10,810 programming, le développement interactif qu'on utilise toujours. 5 00:00:12,200 --> 00:00:14,710 On va se poser la question qu'est-ce qui se passe quand 6 00:00:15,210 --> 00:00:18,660 je recompile une classe, quelles sont les opérations 7 00:00:18,860 --> 00:00:23,070 réflexives mises en jeu pour implémenter tout ce qui est 8 00:00:23,270 --> 00:00:25,200 nécessaire pour le développement interactif? 9 00:00:25,400 --> 00:00:26,160 Je vais prendre un scénario. 10 00:00:26,780 --> 00:00:29,900 Typiquement le scénario standard, c'est je définis une 11 00:00:30,100 --> 00:00:33,000 classe, j'ajoute des méthodes sur cette classe, je crée 12 00:00:33,200 --> 00:00:36,950 des instances de cette classe et ensuite, je vais changer 13 00:00:37,150 --> 00:00:38,860 la définition de la classe et je vais rajouter des 14 00:00:39,060 --> 00:00:42,030 nouvelles variables d'instances dans cette classe. 15 00:00:42,230 --> 00:00:43,520 Donc ça veut dire qu'est-ce que je dois faire des 16 00:00:43,720 --> 00:00:46,550 instances qui existent déjà, qui ont été créées, alors 17 00:00:46,750 --> 00:00:48,050 qu'il y avait un attribut de moins dans la classe. 18 00:00:48,600 --> 00:00:51,860 Ça veut dire qu'il va falloir être capable de migrer ces instances. 19 00:00:52,610 --> 00:00:55,030 Il faut mettre en place un mécanisme pour transférer ces 20 00:00:55,230 --> 00:00:57,350 instances sur la nouvelle version de la classe. 21 00:00:57,690 --> 00:00:58,760 Et ensuite, je continue à travailler. 22 00:00:59,430 --> 00:01:00,900 C'est ce qu'on va voir dans la suite. 23 00:01:01,220 --> 00:01:05,280 On va voir les opérations qui permettent la recompilation 24 00:01:05,480 --> 00:01:08,460 dynamique de classes, qui permettent la recompilation de 25 00:01:08,660 --> 00:01:13,210 méthodes, qui vont permettre de migrer automatiquement et 26 00:01:13,410 --> 00:01:16,400 de façon transparente pour le programmeur les instances 27 00:01:16,600 --> 00:01:18,070 sur la nouvelle version de la classe. 28 00:01:18,640 --> 00:01:21,260 Pour ça, il va falloir que je sois capable de récupérer 29 00:01:21,460 --> 00:01:23,610 toutes les instances d'une classe, et ensuite que je sois 30 00:01:23,810 --> 00:01:27,640 capable de changer tous ceux qui utilisaient ces 31 00:01:27,840 --> 00:01:31,000 objets-là, de façon à leur changer leur pointeur pour 32 00:01:31,200 --> 00:01:32,000 qu'ils utilisent les nouveaux objets. 33 00:01:34,180 --> 00:01:35,920 Alors on commence par le début. 34 00:01:36,230 --> 00:01:37,890 Comment est-ce que je fais pour récupérer toutes les 35 00:01:38,090 --> 00:01:41,080 instances d'une classe? J'ai un message pour ça, si j'envoie 36 00:01:41,280 --> 00:01:45,060 allInstances à une classe, ça me rend bien une collection 37 00:01:45,260 --> 00:01:47,180 de tous les objets instances de cette classe. 38 00:01:47,840 --> 00:01:50,350 C'est vraiment une collection de tous les objets, je peux les manipuler. 39 00:01:50,550 --> 00:01:51,330 Ce sont des objets standards. 40 00:01:51,730 --> 00:01:54,000 Par exemple, si je demande allInstances à la classe 41 00:01:54,200 --> 00:01:56,050 Window, j'obtiens bien une collection d'objets Window. 42 00:01:58,250 --> 00:02:00,260 Je peux récupérer le premier élément de la collection et 43 00:02:00,460 --> 00:02:02,510 lui envoyer le message close puisque c'est une fenêtre. 44 00:02:03,590 --> 00:02:06,540 Dans la classe window, il y a bien la méthode close qui est définie. 45 00:02:07,840 --> 00:02:12,290 On a une autre primitive réflexif qui est pointersTo, qui 46 00:02:12,490 --> 00:02:14,870 permet quand j'envoie ce message-là à un objet de 47 00:02:15,070 --> 00:02:19,080 récupérer la collection de tous les objets qui détiennent 48 00:02:19,280 --> 00:02:20,420 une référence sur cet objet. 49 00:02:21,210 --> 00:02:23,840 Ça va être extrêmement pratique quand on va vouloir 50 00:02:24,590 --> 00:02:27,220 changer des objets. 51 00:02:29,660 --> 00:02:30,560 Justement on y vient. 52 00:02:31,820 --> 00:02:35,050 Pour changer tous les pointeurs, c'est la primitive become. 53 00:02:35,250 --> 00:02:39,710 L'idée, ça va être de dire je veux échanger 2 objets. 54 00:02:41,310 --> 00:02:44,360 Pour échanger 2 objets et de façon symétrique, c'est-à-dire 55 00:02:44,600 --> 00:02:47,500 que tous ceux qui pointaient sur cet objet-là dans le 56 00:02:47,700 --> 00:02:50,430 système, et là on avait d'autres pointeurs sur cet 57 00:02:50,630 --> 00:02:53,400 objet-là, je vais inverser. Je vais dire maintenant tous 58 00:02:53,600 --> 00:02:57,870 ces éléments pointent sur celui-là et tous ceux qui 59 00:02:58,070 --> 00:03:02,910 étaient ici, je casse ces liens, ils vont pointer 60 00:03:03,110 --> 00:03:07,820 sur cet objet-là. C'est ça le become, c'est 61 00:03:08,020 --> 00:03:11,510 inverser tous les pointeurs dans le système symétriquement. 62 00:03:13,410 --> 00:03:15,000 Je vous donne un exemple ici. 63 00:03:15,650 --> 00:03:19,080 Je crée un point 1 qui pointe vers l'objet. 64 00:03:19,540 --> 00:03:24,440 00, une variable pt2 qui pointe vers l'objet 00 aussi 65 00:03:24,640 --> 00:03:29,280 ici, une variable pt3 qui pointe vers le point 100 66 00:03:29,480 --> 00:03:32,500 100. Et puis j'écris la primitive pt1 become pt3. 67 00:03:32,700 --> 00:03:36,670 Ça veut dire que tous ceux qui pointaient sur l'objet 68 00:03:36,870 --> 00:03:41,330 pointé par la variable pt1, maintenant ils vont pointer vers le point 3. 69 00:03:44,750 --> 00:03:49,160 On voit que la variable pt2 pointait bien sur le même 70 00:03:49,360 --> 00:03:52,820 objet que pt1 et maintenant elle pointe vers celui qui 71 00:03:53,020 --> 00:03:55,510 était référencé par pt3. Pt3 lui, vu que c'est 72 00:03:55,710 --> 00:03:59,870 symétrique, ça va pointer sur ce qui était pointé par 73 00:04:00,070 --> 00:04:01,600 pt1, c'était celui-là, là-haut. 74 00:04:03,270 --> 00:04:05,650 Et pt1 pointe bien vers celui qui était pointé par pt3. 75 00:04:06,240 --> 00:04:09,860 On a bien les pointeurs qui ont été échangés de façon symétrique. 76 00:04:11,250 --> 00:04:15,640 On a le pendant asymétrique qui est becomeForward qui 77 00:04:15,840 --> 00:04:18,110 permet de dire que je veux changer les pointeurs. 78 00:04:18,420 --> 00:04:21,350 Tous ceux qui pointaient vers cet objet-là vont 79 00:04:21,550 --> 00:04:24,940 maintenant pointer sur cet objet-là, mais pas l'inverse. 80 00:04:25,140 --> 00:04:26,400 C'est-à-dire ceux qui pointaient sur cet objet-là 81 00:04:26,600 --> 00:04:28,000 continuent à pointer sur cet objet-là. 82 00:04:31,650 --> 00:04:34,080 Je vous donne encore un exemple, le même exemple avec des 83 00:04:34,280 --> 00:04:38,880 points où ici on effectue le becomeForward, et ce qu'on 84 00:04:39,080 --> 00:04:43,180 peut voir c'est que ceux qui ont été impactés, ce sont 85 00:04:43,380 --> 00:04:47,880 vraiment pt1 et pt2, pt3 n'a pas du tout été impacté par le becomeForward. 86 00:04:49,000 --> 00:04:52,690 Tous ceux qui pointaient sur cet objet là n'ont pas été changés. 87 00:04:55,650 --> 00:04:59,690 On a une autre primitive réflexive qui est adoptInstance, 88 00:05:00,180 --> 00:05:02,510 qui permet de changer la classe d'un objet. 89 00:05:02,810 --> 00:05:05,910 En gros je demande à une classe d'adopter une nouvelle 90 00:05:06,110 --> 00:05:10,640 instance passée en paramètre. Changer la classe d'un 91 00:05:10,840 --> 00:05:13,290 objet c'est extrêmement puissant mais ça a ses limites. 92 00:05:13,490 --> 00:05:16,070 Il faut absolument que la classe de départ de l'objet, 93 00:05:16,400 --> 00:05:21,220 donc la classe de anInstance, soit compatible du 94 00:05:21,420 --> 00:05:24,490 point de vue du format avec la classe receveur ici. 95 00:05:24,860 --> 00:05:27,350 Je ne peux pas changer n'importe quel objet, par exemple 96 00:05:27,550 --> 00:05:29,570 en collection si ce n'en est pas une, et caetera, ça 97 00:05:29,770 --> 00:05:31,520 dépend si c'est une collection indexée, et caetera. 98 00:05:32,060 --> 00:05:35,650 Le format des objets va avoir une importance. 99 00:05:36,900 --> 00:05:38,620 En fait une classe, qu'est-ce que c'est? 100 00:05:38,820 --> 00:05:40,680 Si on revient à l'essence des classes. 101 00:05:41,190 --> 00:05:43,100 Une classe, ce n'est ni plus ni moins qu'un format. 102 00:05:44,630 --> 00:05:47,110 Le format va spécifier le nombre de variables d'instances, 103 00:05:47,480 --> 00:05:50,390 le type de ces variables, nommer, indexer, vous vous 104 00:05:50,590 --> 00:05:52,440 souvenez on l'a vu dans une séquence précédente. 105 00:05:53,160 --> 00:05:55,640 Une classe a une superclass et un dictionnaire des méthodes. 106 00:05:57,250 --> 00:06:00,580 On peut voir sur la classe Behavior, la classe Behavior c'est 107 00:06:00,780 --> 00:06:04,390 une superclass de la classe class qui définit le 108 00:06:04,590 --> 00:06:07,430 comportement de base des classes. 109 00:06:08,150 --> 00:06:10,700 Je vous montre juste le minimum vital qu'on doit avoir 110 00:06:10,900 --> 00:06:12,870 dans une classe, une classe a une superclass, un 111 00:06:13,070 --> 00:06:15,940 dictionnaire de méthode et elle spécifie un ensemble de 112 00:06:16,140 --> 00:06:17,390 format pour ses instances. 113 00:06:19,540 --> 00:06:21,740 Dans cet exemple, je vais résumer un peu tout ce qu'on a 114 00:06:21,940 --> 00:06:24,380 vu jusqu'à maintenant, tous les comportements réflexifs 115 00:06:25,000 --> 00:06:29,780 que l'on a vus pour doter certaines 116 00:06:30,000 --> 00:06:32,670 instances d'un comportement spécifique, qui leur est propre. 117 00:06:32,870 --> 00:06:37,550 Je vous explique le code au fur et 118 00:06:38,080 --> 00:06:40,470 à mesure. Ici, sur les trois premières lignes, on va 119 00:06:40,670 --> 00:06:44,110 créer une instance de la classe behavior, je vous rappelle c'est une classe. 120 00:06:47,380 --> 00:06:50,000 Je crée une instance de cette classe que j'appelle 121 00:06:51,690 --> 00:06:53,700 behavior, donc là j'ai un objet behavior. 122 00:06:55,050 --> 00:06:59,550 Je vais indiquer que cet objet behavior hérite de la 123 00:06:59,750 --> 00:07:04,310 classe Model. Je fixe le 124 00:07:04,510 --> 00:07:07,480 format de cet objet behavior. 125 00:07:07,680 --> 00:07:10,920 Ensuite, je crée une instance de la classe Model. 126 00:07:11,620 --> 00:07:15,710 J'ai un objet Model. 127 00:07:17,240 --> 00:07:21,080 Et puis, la ligne importante de ce morceau de code est ici. 128 00:07:21,280 --> 00:07:24,590 Je vais changer la classe de cet objet Model pour être la 129 00:07:24,790 --> 00:07:27,490 classe de l'objet passé en paramètre, donc behavior. 130 00:07:27,690 --> 00:07:31,790 Maintenant, je vais casser ce lien et je vais dire tu 131 00:07:32,000 --> 00:07:34,310 deviens une instance de cette classe. 132 00:07:34,850 --> 00:07:37,000 Cette classe est bien une sous-classe de la classe de Model. 133 00:07:38,790 --> 00:07:41,000 Je vais être capable maintenant de compiler une nouvelle 134 00:07:41,200 --> 00:07:45,320 méthode dans cette classe. Je vais compiler la méthode foo qui retourne 2. 135 00:07:49,140 --> 00:07:51,140 Et c'est bien dans l'objet behavior que je fais ça. 136 00:07:54,490 --> 00:07:57,800 On voit que la ligne importante, si j'envoie le message 137 00:07:58,000 --> 00:08:03,000 foo à mon objet Model, il 138 00:08:03,200 --> 00:08:04,010 va bien me répondre 2. 139 00:08:04,610 --> 00:08:07,590 Pourquoi? Si vous vous souvenez de la méthode look-up, 140 00:08:07,790 --> 00:08:09,860 j'envoie le message foo à l'objet Model, l'objet Model 141 00:08:10,400 --> 00:08:13,170 commence la recherche de la méthode à exécuter dans sa 142 00:08:13,370 --> 00:08:15,520 classe qui est l'objet behavior; et dans son dictionnaire 143 00:08:15,720 --> 00:08:17,840 de méthode il y a bien la méthode foo. 144 00:08:18,040 --> 00:08:18,800 Ça marche parfaitement. 145 00:08:19,880 --> 00:08:22,070 Par contre, si je crée une nouvelle instance de la classe 146 00:08:22,270 --> 00:08:27,130 Model, je lui envoie le 147 00:08:27,330 --> 00:08:31,770 message foo. Donc là, j'ai une erreur qui est signalée, 148 00:08:32,080 --> 00:08:35,000 message not understood puisque si j'applique l'algorithme 149 00:08:35,200 --> 00:08:38,270 du look-up, je remonte dans la classe de cet objet, c'est 150 00:08:38,470 --> 00:08:41,510 la classe Model et dans son dictionnaire de méthode, il n'y 151 00:08:41,710 --> 00:08:43,080 a pas la méthode foo, ni dans aucune des superclass. 152 00:08:46,750 --> 00:08:49,680 Ce mécanisme un peu avancé nous a permis de doter une 153 00:08:49,880 --> 00:08:53,330 instance spécifique de la classe modèle d'un comportement particulier. 154 00:08:53,530 --> 00:08:57,670 Je reprends le même exemple sur la classe Set pour que vous compreniez bien. 155 00:08:58,050 --> 00:09:02,130 Je crée set1, c'est une instance de la classe Set. 156 00:09:03,090 --> 00:09:05,340 La classe set a un dictionnaire de méthode avec la méthode add. 157 00:09:06,760 --> 00:09:09,940 Je veux, pour une instance de la classe Set particulière, 158 00:09:10,290 --> 00:09:12,450 changer cette méthode add qui a un comportement 159 00:09:12,650 --> 00:09:15,270 particulier spécifique pour un set particulier. 160 00:09:15,480 --> 00:09:19,570 Donc, je vais créer une classe anonyme, une classe 161 00:09:19,770 --> 00:09:23,440 particulière, mon objet behavior d'avant qui hérite de la classe Set. 162 00:09:23,640 --> 00:09:25,010 C'est de l'héritage. 163 00:09:25,600 --> 00:09:30,300 Je vais avoir mon objet set2 qui est instance de cette 164 00:09:30,500 --> 00:09:34,150 classe anonyme. Dans le dictionnaire de méthode de cette 165 00:09:34,350 --> 00:09:37,500 classe anonyme, je vais redéfinir la méthode add en lui 166 00:09:37,700 --> 00:09:40,470 mettant du comportement spécifique, le comportement qui va m'intéresser. 167 00:09:41,000 --> 00:09:45,130 Et maintenant si j'envoie le message add à cet objet 168 00:09:45,330 --> 00:09:48,110 set2, c'est bien cette méthode qui va être sélectionnée, 169 00:09:49,160 --> 00:09:53,760 qui va avoir un comportement spécifique et différent de 170 00:09:54,000 --> 00:09:57,630 la méthode add, si j'envoyais le message add à ce set-là. 171 00:09:58,290 --> 00:10:01,720 On a bien 2 Set qui n'auront pas le même comportement 172 00:10:02,070 --> 00:10:03,840 face au message add. 173 00:10:04,040 --> 00:10:07,000 Je vous montre le code pour réaliser cette expérience. 174 00:10:08,360 --> 00:10:11,250 Exactement comme avant, je vais créer une classe, 175 00:10:11,880 --> 00:10:14,850 instance de la classe behavior. Je vais fixer la 176 00:10:15,050 --> 00:10:18,570 super-classe à la classe Set. Je fixe le format. 177 00:10:18,830 --> 00:10:23,420 Je vais compiler la méthode add dans cette classe anonyme. 178 00:10:24,280 --> 00:10:27,660 La méthode add, je viens redéfinir la méthode add qui est 179 00:10:27,860 --> 00:10:29,260 définie dans la classe Set. 180 00:10:30,110 --> 00:10:32,610 Je fais un Transcript show juste pour faire un affichage, 181 00:10:32,810 --> 00:10:35,920 pour dire que la méthode a été exécutée avec tel objet. 182 00:10:36,360 --> 00:10:39,640 Je l'appelle super pour exécuter la méthode add qui se 183 00:10:39,840 --> 00:10:40,600 trouve dans la classe Set. 184 00:10:41,370 --> 00:10:43,210 Donc maintenant on va tester ce code. 185 00:10:43,840 --> 00:10:45,530 Pour faire ça, je vais créer un premier Set. 186 00:10:45,730 --> 00:10:48,500 Je vous rappelle j'ai la classe Set. 187 00:10:50,210 --> 00:10:53,590 Je vais créer une première instance de cette classe que j'appelle Set. 188 00:10:53,790 --> 00:10:56,780 Cette instance, je vais lui envoyer le message add1. 189 00:11:00,000 --> 00:11:03,480 Donc là, c'est bien la méthode add de la classe Set qui va être utilisée. 190 00:11:05,290 --> 00:11:08,460 Si je demande cette classe, je vais bien obtenir la classe Set. 191 00:11:09,360 --> 00:11:12,310 Sauf que maintenant je vais exécuter cette primitive. 192 00:11:12,510 --> 00:11:15,480 Donc, je vais demander à cet objet set de changer sa 193 00:11:15,680 --> 00:11:19,260 classe et d'utiliser comme classe la classe Behavior que 194 00:11:19,460 --> 00:11:22,830 j'avais avant, qui était une sous-classe de la classe 195 00:11:23,030 --> 00:11:24,590 Set, donc Behavior. 196 00:11:24,790 --> 00:11:27,180 On va le changer maintenant il va devenir une instance de 197 00:11:27,380 --> 00:11:30,140 cette classe, qui lui, a une nouvelle version de la méthode add. 198 00:11:30,450 --> 00:11:31,330 Ce lien a été cassé. 199 00:11:31,530 --> 00:11:35,620 Si j'envoie maintenant le message add2 200 00:11:37,090 --> 00:11:41,090 à cet objet, ça va sélectionner la méthode add de mon 201 00:11:41,290 --> 00:11:43,260 objet Behavior, qui est une sous-classe de la classe set 202 00:11:43,620 --> 00:11:45,140 et ça va bien exécuter ce code-là. 203 00:11:47,620 --> 00:11:52,460 Je vais voir sur le transcript que la méthode a été 204 00:11:52,660 --> 00:11:56,730 exécutée. Par contre, l'action va bien être exécutée in 205 00:11:56,930 --> 00:11:59,540 fine puisque jusqu'ici super pour aller exécuter la 206 00:11:59,740 --> 00:12:03,000 méthode add aussi de la classe Set. 207 00:12:03,190 --> 00:12:06,470 Ça m'a permis de doter ou d'espionner les instances de 208 00:12:06,670 --> 00:12:09,040 Set que j'aurais sélectionnées grâce à cette ligne-là. 209 00:12:09,240 --> 00:12:10,000 En conclusion. 210 00:12:12,590 --> 00:12:14,840 Tous les outils de réflexions qu'on a vu jusqu'à 211 00:12:15,040 --> 00:12:17,000 maintenant sont extrêmement utiles, et permettent la 212 00:12:17,200 --> 00:12:19,000 construction d'outils novateurs et innovants. 213 00:12:19,390 --> 00:12:23,000 On peut vraiment expérimenter d'implémenter des nouvelles 214 00:12:23,200 --> 00:12:25,710 choses grâce à ces primitives réflexives. 215 00:12:26,210 --> 00:12:28,660 Ça promeut l'extensibilité du langage. 216 00:12:30,680 --> 00:12:33,480 Par contre, il y a une règle d'or, quand on utilise la 217 00:12:33,680 --> 00:12:36,640 réflexion il ne faut absolument pas l'utiliser à mauvais 218 00:12:36,840 --> 00:12:38,600 escient dans vos programmes, dans du code métier. 219 00:12:39,000 --> 00:12:43,170 Dans du code métier, on va limiter l'utilisation des 220 00:12:43,370 --> 00:12:45,380 codes réflexifs puisque je vous ai dit qu'avec la 221 00:12:45,580 --> 00:12:48,490 réflexion, on peut aller trop loin, violer l'encapsulation 222 00:12:48,690 --> 00:12:53,340 des objets et vraiment s'affranchir des règles de la 223 00:12:53,540 --> 00:12:54,770 bonne pratique de la programmation objet. 224 00:12:55,000 --> 00:12:57,700 Donc c'est vraiment réservé à la construction d'outils.