4.4.7 les variables de classe
Les variables de classe sont des variables
globales à une classe et ses sous-classes. Elles sont déclarées lors de la définition
d’une classe avec le paramètre classVariableNames:. Voici, pour exemple, la
définition de la classe Character du système SQUEAK:
Magnitude subclass: #Character
instanceVariableNames: ’’
classVariableNames: ’CharacterTable
ClassificationTable
LetterBits
LowercaseBit
UppercaseBit’
poolDictionaries: ’’
category: ’Collections-Text
|
|
Cette classe définit cinq variables de classes, les arguments du paramètre
classVariableNames:. Ces variables sont accessibles à la classe même, ainsi qu’à
toute instance de cette classe et de ses sous-classes éventuelles, donc à tout
caractère. À l’extérieur de la classe et de ses sous-classes les variables de classe
sont inconnues et leur consultation produit une erreur variable indéfinie. La
portée d’une telle variable est donc considérablement plus restreinte que celle
d’une variable globale: seulement la classe et des instance de la classe (et les
sous-classes et instances des sous-classes de la classe), dans laquelle une
variable de classe est définie, peuvent consulter et modifier ses variables de
classe.
Pour exemple, reprenons notre classe Test définie plus haut et supposons
qu’aucune instance de la classe devrait avoir plus que 10 éléments. Sachant que
les méthodes de création avec les variantes du message with: se limitent à créer
des instances avec au maximum six éléments, nous n’avons pas besoin
de toucher à cet ensemble de méthodes. La seule méthode de création
susceptible de produire des instances de taille arbitraire est la méthode new:.
Nous pouvons la redéfinir, comme méthode de classe, dans la classe Test
comme:
new: n
n > 10
ifTrue: [self error: ’trop d’éléments’]
ifFalse: [super new: n]
|
|
et, à partir de la compilation de cette méthode, si l’on veut créer une instance de
la classe Test de plus que dix éléments, bien entendu, la transmission du message
error:51
se déclenche et aucune instance ne sera créée.
Si nous décidons par la suite que nous ne pouvons admettre que des instances
ayant moins que 16 éléments, nous pouvons modifier cette méthode, en
remplaçant l’entier 10 par l’entier 15 et recompiler la méthode. Ce n’est pas très
élégant, surtout, si la décision est le résultat d’un calcul et la modification devrait
se faire automatiquement. Dans ce cas, il serait probablement plus simple
d’ajouter une variable de classe, nommée par exemple NombreMaxDElements, à la
définition de notre classe Test,
ArrayedCollection variableSubclass: #Test
instanceVariableNames: ’’
classVariableNames: ’NombreMaxDElements’
poolDictionaries: ’’
category: ’Cours-Smalltalk’
|
|
et de l’initialiser à la valeur voulue avec une transmission utilisant la méthode
ci-dessous:52
nombreMaxDElements: n
NombreMaxDElements := n
|
|
Ensuite il faut remplacer la méthode new: ci-dessus par:
new: n
n > NombreMaxDElements
ifTrue: [self error: ’trop d’éléments’]
ifFalse: [super new: n]
|
|
Ainsi, chaque fois que vous décidez d’un autre nombre d’éléments maximal, il
suffit de changer la valeur de la variable de classe NombreMaxDEelements en
ce nouveau nombre et il n’est plus nécessaire de recompiler la méthode
new:.
L’exemple ci-dessus était une utilisation possible d’une variable de classe.
Nous aurions, comme nous l’avons indiqué, pu faire la même chose sans utiliser ce
type de variable, sans utiliser une variable partagée: de manière moins élégante,
certes, mais de manière aussi efficace.
Supposons alors que nous voulions qu’il n’existe jamais plus d’instances que le
nombre maximal d’éléments qu’elles peuvent contenir. Comment faire sans
utiliser une variable partagée? Pour résoudre ce problème, il faudrait qu’à
chaque création d’une instance nous gardions un souvenir de ce fait et
qu’après NombreMaxDElements créations nous interdisions toute création
ultérieure.53
Commençons alors par créer une variable de classe supplémentaire nommée
NombreDInstances:
ArrayedCollection variableSubclass: #Test
instanceVariableNames: ’’
classVariableNames: ’NombreMaxDElements
NombreDInstances’
poolDictionaries: ’’
category: ’Cours-Smalltalk’
| |
qui devra contenir à tout instant le nombre d’instances créées avec le message
new: propre à la classe Test. Bien entendu, nous devons aussi modifier le message
new: pour qu’il réponde à ces nouvelles contraintes: il faut qu’il vérifie à la
fois que la nouvelle instance ne contienne pas trop d’éléments et que le
nombre total d’instances ne soit pas supérieur au nombre d’éléments
permis. Dans la foulée, implémentons l’initialisation de la variable de
classe NombreDInstance également dans cette nouvelle méthode new:. La
voici:
new: n
n > NombreMaxDElements
ifTrue: [self error: ’trop d’éléments’].
NombreDInstances == nil
ifTrue [NombreDInstances := 1].
NombreDInstances = NombreMaxDElements
ifTrue: [self error: ’trop d’instances’]
ifFalse: [NombreDInstances := NombreDInstances + 1
super new: n]
| |
Notez l’utilisation de la pseudo-variable super dans nos deux définitions de
new:. Dans les deux cas elle sert à demander à SQUEAK de lancer ses
“vraies” procédures de création d’instances après avoir fait nos opérations de
vérification!