Mécanique Smalltalk - (2)






La construction d'objets permet de matérialiser un modèle. Le modèle est une représentation abstraite d'un phénomène, d'une situation, d'une structure ou d'un problème concret. Cette matérialisation permet une manipulation des objets, une validation ou au contraire une remise en cause du modèle au travers d'expériences. Si le modèle est validé, il peut directement servir à une transcription dans le domaine informatique d'opérations prenant place dans le monde réel.

Que ceci n'empêche pas d'utiliser les objets pour représenter des mondes imaginaires !

V/ Types de messages



Le langage Smalltalk utilise trois types de messages présentés ici selon l'ordre de précédance :

Les messages unaires


Les messages unaires ne font apparaître qu'un seul objet dans la transmission :
le récepteur. Par exemple le message #class transmis dans l'expression Integer class est un message unaire. On peut envoyer les messages unaires en séquence, les messages sont traités de gauche à droite, chaque message est envoyé au résultat de la transmission précédente.
Par exemple Display bounds width squared est une expression Smalltalk exécutée comme suit : le message #bounds est envoyé à l'objet Display (celui-ci représente l'écran), la réponse est une instance de la classe Rectangle (donnant les coordonnées des points extrêmes de l'écran, en haut à gauche et en bas à droite). Le message #width envoyé à ce rectangle lui demandera sa largeur (la différence entre les abscisses des bords verticaux), cette largeur, une instance de nombre (instance d'une sous-classe directe ou non de la classe Number), recevra alors le message #squared, qui délivrera enfin le résultat final de l'expression correspondant à la proposition "Le carré de la largeur du cadre de l'écran", ou, en anglais, "Display bounds width squared". On peut voir ici que l'anglais, étant un langage très primitif, est beaucoup plus proche de Smalltalk que le français ne l'est !

Dans une séquence, les messages sont effectués de gauche à droite, et chacun est envoyé au résultat du message précédent. Par contre, dans une cascade de messages, les messages sont tous envoyés au même récepteur :

Transcript cr; cr; tab


Cette expression envoie deux fois le message #cr puis le message #tab à l'objet Transcript. Dans une cascade les messages sont séparés par des points-virgules. L'objet Transcript représente la fenêtre de texte, ouverte au lancement de Smalltalk, collectant les messages du système et les traces textuelles des programmes. Le message #cr (initiales de Carriage Return) impose un saut de ligne et le message #tab écrit une tabulation. Transcript est une instance de TextCollector.

Les cascades ne sont intéressantes que si elles provoquent un effet de bord, c'est à dire une modification de l'état d'un objet ayant une répercussion dépassant le cadre strictement littéral de l'expression. Ici, l'état du texte et de l'écran sont modifiés après l'exécution de l'expression. C'est d'ailleurs cette modification qui est importante. Le résultat, c'est-à-dire la valeur de retour de la cascade, qui est la valeur du dernier message transmis, est en général ignoré. Lors que, dans l'expression précédente, l'état de l'objet Display n'était par modifié, et que l'important était la valeur de retour de chaque message.

Un contre exemple, où la valeur de retour de la cascade est utilisée :

(Set new add: 1; add: 3; add: 4; yourself) includes: 4

Le message #new instancie la classe Set, c'est-à-dire crée un nouvel objet représentant un ensemble, puis la cascade est destinée à modifier cet objet en lui ajoutant des éléments (avec le message #add:) ; sauf pour le dernier message de la cascade, #yourself, qui ne provoque pas d'effet de bord, et qui est justement placé dans cette position finale pour que le résultat de la cascade en soit son récepteur, c'est-à-dire le nouvel ensemble. Ceci est nécessaire car le message #add: renvoie en valeur non pas le récepteur, mais son argument.

Les messages binaires


Les messages binaires mettent en jeu deux objets : le récepteur et un argument. Les sélecteurs de message des messages binaires sont formés de 1 ou 2 caractères spéciaux. par exemple :
	1 + 2

	21 // 5

Le premier message #+ effectue l'addition, le second effectue la division entière. Autres exemple :

#(A B C), #(1 2 3) 'Il était ', 'une fois'

La virgule est le message de concaténation (du latin catena, chaîne), elle permet de concaténer (coller bout-à-bout) deux tables ou deux chaînes de caractères. Un peu comme la fonction append du langage Lisp.
Les messages binaires sont exécutés après les messages unaires et avant les messages à mots-clefs. Il sont exécutés de gauche à droite. Par exemple :

3 + 4 * 5 -> 35

Pour imposer un ordre d'évaluation différent, les parenthèses peuvent être utilisées.

Les messages à mots-clefs


Les messages à mots-clefs sont n-aires (il prennent un nombre quelconque non nul d'arguments). Chaque argument est introduit par un mot-clef.

Par exemple :


10 between: 0 and: 45 0 to: Float pi by: Float pi / 4

VI/ Types de variables

Les variables globales


Les globales ont la plus grande portée : elles sont accessibles depuis n'importe quel endroit de Smalltalk (toute méthode, tout bloc). Les variables globales sont rangées dans le dictionnaire système nommé Smalltalk. Pour affecter une variable globale, on peut écrire :

Smalltalk at: #VariableGlobale put: valeur

Pour la lire, il suffit d'écrire son nom. Pour la supprimer il faut utiliser le message #removeKey:.

Les variables globales doivent avoir une majuscule initiale.

Tous les noms des classes sont des variables globales qui pointent vers la classe correspondante.
Il est fortement déconseillé d'utiliser des variables globales dans un programme smalltalk, à part, implicitement, pour nommer les classes d'objets. Mais les techniques d'écriture de programme en langage objet permettent d'éviter d'avoir recours à ce type de variable. Pourquoi les éviter ? Parce que c'est une source d'erreur d'avoir un objet accessible de toutes parts d'un programme, c'est aussi cause de conflit de nommage des objets. C'est aussi la cause de persistance dans la mémoire d'objets devenus inutiles. Habituellement, on les utilise pour la mise au point, pour conserver une valeur au-delà de la simple exécution d'une expression. Mais, les workspaces de VisualWorks ont maintenant leurs propres dictionnaires de variables, et les globales deviennent inutiles (mis à part le nommage des classes et de quelques objets particuliers tels que true, false et nil).

Les variables temporaires



Les temporaires ont une portée limitée au texte de la méthode (ou du bloc) dans laquelle elles sont déclarées. Leur durée de vie est limitée au temps d'exécution de la méthode. Dans une méthode récursive par exemple, autant d'exemplaires de la variable sont construits qu'il y a d'exécutions en cours (alors qu'une variable globale désignerait toujours le même objet). La mémoire pour les variables temporaires est allouée dans l'équivalent d'une pile (les temporaires sont comme les variables automatiques du langage C).

Les temporaires doivent avoir une minuscule initiale.

Les paramètres



Les paramètres ont également une portée limitée au texte de la méthode dans laquelle ils sont déclarés. Leur durée de vie est également limitée au temps d'exécution de la méthode. Mais à la différence des temporaires, la valeur des paramètres est déterminée par les arguments donnés au moment de la transmission du message, et ne peuvent pas être changés. Les paramètres des méthodes (et des blocs) sont uniquement accessibles en lecture.

Les pseudo-variables



Les pseudo-variables ne peuvent pas non plus être affectées, leurs valeurs sont attribuées par le système en fonction du contexte. Par exemple : self désigne toujours le récepteur du message dans le corps de la méthode correspondante.

Les variables d'instance



Les variables d'instance sont allouées au niveau des instances. Leur portée est l'ensemble des méthodes d'instance de la classe correspondante et de ses sous-classes.

Les variables de classe



Les variables de classe sont partagées par tous les objets d'une classe et de ses sous-classes. Leur portée est l'ensemble des méthodes de classe et d'instance de ses classes.

Les variables de classe commencent par une majuscule.

Les variables d'instance de métaclasse



La pluspart du temps, il est préférable d'utiliser les variables d'instance de métaclasse (VIMC) aux variables de classe lorsque l'on veut rendre disponible un objet à tous les objets d'une classe en laissant une chance aux sous-classes de redéfinir leurs propres valeurs. Mais la portée des VIMC est limitée aux méthodes de classe (de la classe et des sous-classes), et si les instances doivent y accéder, des méthodes d'accès doivent être définies au niveau de la classe. En effet les variables de classe ne sont allouées qu'une seule fois au niveau de la classe où elles sont définies, et si une sous-classe modifie la valeur de la variable, cette modification se répercute pour toutes les autres classes. Les VIMC sont allouées pour chaque classe héritant de la définition, donc chaque classe peut gérer sa propre valeur pour la variable.

VII/ Les collections


Nous avons vu un certain nombre de messages que les collections pouvaient recevoir. Je les récapitule ici, avec les messages d'instanciation et d'accès correspondant aux collections les plus courantes.