Bonjour à tous. Dans cette séquence, nous allons voir
quelques points avancés sur les classes.
3 points, notamment comment on va partager de l'état
entre plusieurs instances d'une même classe et de ses sous-classes.
Deuxième point, on va voir un point qui permet aux
classes d'avoir leur propre variable.
Et enfin, on va voir comment initialiser les classes.
Premier point, partager de l'état entre plusieurs instances.
On a plusieurs instances d'une même classe, et toutes ces
instances veulent avoir un objet commun pour partager de l'état.
En Java, on utilise une variable d'instance avec le
mot-clé statique. La valeur d'une variable
statique est la même pour toutes les instances.
En Pharo, on utilise les variables de classes.
Vous avez pu voir cette expression répétée encore et
encore qui permet de créer une classe.
Là, on crée la classe color, on dit que c'est une sous-classe d'objects.
On liste ses variables d'instances et cette ligne-là à
laquelle on n'a pas trop prêté attention jusqu'à
maintenant liste les variables de classes de la classe color.
On voit que la classe color a au moins 2 variables
colorRegistry et componentMask.
Les valeurs d'une variable de classes sont partagées
entre toutes les instances d'une classe et toutes les
instances des sous-classes. Ces variables sont
accessibles à la fois à des méthodes côté instances et
des méthodes côté classes et ces variables prennent une majuscule.
Si je reviens à l'exemple de color, ici, j'ai ma classe
color qui est une instance de la classe Color class, donc
Color class c'est la méta classe.
On a la classe Color qui définit 2 variables d'instances
standards, rgb et alpha.
Ces variables d'instances sont privées
dans le sens où elles sont accessibles seulement depuis
les méthodes de la classe Color.
Et ColorRegistry, on voit que c'est une variable qui a 1,
une majuscule 2, qui est soulignée.
Ça indique que cette variable est une variable de
classes et qu'elle est accessible non seulement depuis
les méthodes de la classe Color, mais aussi depuis les
méthodes de la classe Color class.
Là, on a des exemples d'utilisation de cette variable-là.
Dans une méthode d'instances, dans la méthode private
good de la classe color, on accède à la variable de
classe avec son nom.
De la même façon, pour mettre une valeur dans cette
variable, c'est juste 2 points égal comme avec n'importe
quelle variable. Et on peut faire ça soit du côté
instances, soit du côté classes. Très fréquemment, les
valeurs de ces variables de classes seront lues à l'
intérieur des méthodes d'instances et seront écrites
à l'intérieur des méthodes de classes.
C'est juste ce que l'on retrouve le plus fréquemment,
rien ne contraint à cette utilisation.
Maintenant, on va parler des variables d'instances de classes.
Ce n'est pas la même chose.
Une classe, c'est un objet comme les autres.
Et comme tous les objets une classe peut avoir ses instances.
Une classe étant décrite par sa méta classe, c'est la
méta classe qui va lister les variables d'instances
auxquelles son unique instance, qui est donc la classe, va pouvoir accéder.
Pour définir une variable d'instances du côté classes,
on appuie sur le bouton classes dans le navigateur de
code, ce qui nous donne cette expression-là.
Là, je suis en train de naviguer dans la classe
RPackageSet et sa méta classe définit une
variable cashePackages.
Ces variables ne sont accessibles que des
méthodes de classes et les variables commencent
toujours par une lettre minuscule.
Là, vous avez un exemple.
La variable cash packages est définie dans la méta classe RPackageSet class.
Ce qui signifie que la classe RPackageSet, qui est une
instance de la méta classe, a une valeur associée à cette
variable. De la même façon, toutes
les instances des sous-classes de RPackageSet class ont
une valeur dédiée pour cette variable, une valeur différente.
Là, il n'y a aucun partage.
Chaque instance de la classe RPackageSet class ou d'une
de ses sous-classes va avoir sa propre valeur pour cette variable-là.
On va bien distinguer les variables de classes et les
variables d'instances côté classes grâce à un exemple.
Là, on va implémenter le design pattern Singleton, dont
le but c'est de garantir qu'on a une et une seule instance d'une classe.
Une solution pour implémenter un Singleton, c'est de
stocker dans une variable l'instance à laquelle
tout le monde va accéder et de désactiver la construction
de nouvelles instances. C'est exactement ce qu'on va
faire dans cet exemple de webServer.
Là, j'utilise une variable d'instance côté
classes, ce qu'on vient de voir.
Nom minuscule, c'est dans la définition de ma méta classe
que je vais définir cette variable-là.
Là, je désactive l'exécution de la méthode new,
personne ne pourra envoyer le message new à la classe web
serveur pour créer une nouvelle instance, il faudra
absolument passer par uniqueInstance, qui renvoie soit
la valeur de la variable si ce n'est pas vide, s'il y a
déjà quelque chose dedans, soit qui met quelque chose
dans cette variable, qui grâce à super new va créer une
nouvelle instance de la classe web serveur et qui va
mettre cette instance dans la variable unique instance.
Le fait d'avoir utilisé des classes instance variable va
faire qu'on a la conséquence suivante.
Chaque sous-classe de la classe webServer va avoir sa
propre valeur pour uniqueInstance.
C'est-à-dire que si web serveur a 3 sous-classes, il va
y avoir en tout 4 valeurs pour la variable
uniqueInstance, donc une pour webServer et une pour
chacune de ces sous-classes-là.
Si maintenant, j'utilise une variable de classes.
Là, on édite la classe côté
instances et on ajoute UniqueInstance avec une majuscule.
De la même façon, j'interdis l'envoi du message new et j'implémente
UniqueInstance, la seule chose qui a changé c'est que j'ai
mis un U majuscule plutôt qu'un u minuscule.
Conséquence de choisir une variable de classe, on a un
singleton pour l'ensemble de la hiérarchie.
Si le webServer a 3 sous-classes, les 3 sous-classes plus
la classe webServer vont partager le même singleton, donc
on aura une instance qui sera partagée par tout le monde.
Maintenant, on va parler d'initiation de classes parce qu'on
peut avoir des variables qui sont partagées entre
plusieurs instances, on peut avoir des variables de
classes, mais à un moment il va bien falloir mettre une
valeur dans ces variables-là. En général, on fait ça au
moment de l'initialisation de la classe.
Les objets sont initialisés lorsque est envoyé le message initialize dessus.
Les classes, c'est pareil, on peut envoyer le message
initialize à une classe et c'est dans le code de la
méthode initialize qu'on va décider comment les variables sont initialisées.
Si je veux initialiser la classe couleurs, j'envoie le
message initialize à la classe Color.
En général, lorsque une classe est chargée dans le
système, grâce au gestionnaire de versions, le message
initialize est automatiquement envoyé à toutes les classes au bon moment.
Donc, il n'y a pas besoin de se préoccuper d'envoyer le
message initialize manuellement à toutes les classes qu'on recherche.
Par contre, si on implémente une classe, le développeur
qui crée une nouvelle classe avec une méthode initialize,
va devoir envoyer ses messages initialize lui-même.
Là, on a un exemple dans la classe Color.
On voit qu'ici on est bien du côté de la méta classe et
on initialise quelques variables de classes ou variables
d'instances côté classes.
Là, il y a les 2 types de variables.
Dans les méthodes initialize, côté classes il ne faut
jamais utiliser super initialize.
On vous a dit que côté instances quand on ajoutait une
méthode initialize, on appelait systématiquement super
initialize pour initialiser toutes les variables d'instances
d'un nouvel objet qui est crée.
Pour les classes par contre, comme les classes existent
dans le système et les super classes existent déjà dans
le système au moment où on envoie initialize, on
n'utilise pas super initialize dans une méthode
initialize côté classes, pour ne pas réinitialiser des
classes qui auraient été déjà initialisées.
Ce que vous devez retenir, c'est que pour partager de l'état
on utilise les variables de classes.
Les classes sont des objets comme les autres qui peuvent
avoir leurs propres variables, et dans ces cas-là on
utilise des variables d'instances côté classes.
Une classe est initialisée en envoyant un message
initialize à la classe, et en implémentant une méthode
initialize côté classes.