
Autres références sur le Web pour SmallTalk
SmallTalk et la programmation objets "pure"
L'environnement SmallTalk
Le langage de SmallTalk
Quelques exemples en SmallTalk
1. exemples élémentaires
2. un bonjour sophistiqué
3. la table de multiplication dans une fenetre
|
Dans les années 90, le développement de programmes informatiques se faisait avec d'un coté un éditeur
de texte, de l'autre un compilateur et un éditeur de liens. Pour des programmes de plusieurs milliers
de lignes, la lisibilité, la relecture et la maintenance de code était un problème.
Un des objectifs de la programmation objets a donc été de gérer ce problème.
L'analyse des sources des programmes a mis en évidence des "écueils conceptuels" tels que la duplication de code et le saucissonnage de code. La duplication de code survient lorsque pour des actions similaires (comme trier un tableau de fournisseurs et trier un tableau de clients), on écrit à peu près les mêmes instructions, à quelques modifications près. Le développement se fait d'ailleurs de façon asynchrone : on écrit par exemple le sous-programme de tri du tableau des clients, on le teste, on le met au point puis on le recopie en changeant de nom et on effectue les quelques modifications nécessaires pour qu'il trie le tableau des fournisseurs. Le saucissonnage de code intervient au contraire lorsqu'on garde un seul sous-programmme pour tout gérer : on passe alors sans arret d'un paquet d'instructions communes à des structures de cas multiples en pagaille. Le saucissonnage intervient aussi lorsqu'on qu'on doit gérer de nombreux paramètres. De plus, la programmation classique des interfaces utilisateurs oblige à un code important car les fenêtres d'applications pour les environnements graphiques utilisent de nombreuses sous-fenetres similaires avec des composants standards comme les boutons, les menus. Chaque fenêtre a son jeu de paramètres multiples comme la taille, la largeur, la hauteur de la fenêtre, le nom de la police de caractère, la taille de la police, les attributs logiques de passage en gras, en souligné, etc. ce qui entraine des listes des paramètres monstrueuses. Face à ces problèmes de taille de code et face donc à l'arrivée des interfaces utilisateurs graphiques, il a fallu repenser le schéma de développement d'une application : au lieu de développer des programmes qui traitent des données, c'est à dire au lieu d'accorder la priorité aux programmes, on a décidé de gérer des objets (données) qui comportent des méthodes (programmes) et donc de se focaliser sur les données. Structurer un programme en sous-programmes était une obligation. Quant aux données, elles pouvaient ne pas être structurées du tout ou être structurées de facon incohérente sans qu'on n'y puisse rien. La programmation objets a donc imposé de gérer les données, en organisant des modèles de données, nommées classes d'objets qui comportent des variables ou "champs" et des sous-programmes nommés "méthodes" dans la terminologie objets. Des actions similaires se voient alors donner le même nom, c'est ce qu'on appelle le polymorphisme. Un comportement défini pour un modèle est automatiquement détecté et repris par un sous-modèle : on dit qu'il y a héritage des actions. De plus, les modèles peuvent avoir des valeurs par défaut. Au lieu de donner tous les paramètres par exemple pour une fenêtre utilisateur, on peut se contenter de ne donner que les valeurs différentes des valeurs par défaut d'où un code plus court. La plupart des langages existant dans les années 90 (comme C et Pascal) ne pouvaient pas implémenter ces concepts directement et on a donc "bricolé" ces langages pour qu'ils puissent gérer les objets. Par exemple, le langage pascal est devenu le langage pascal orienté objets (comme dans Turbo Pascal pour Windows) : un objet est simplement défini comme un type particulier qui permet de coiffer les autres déclarations... Signalons qu'avec l'arrivée des développements visuels (on compose à l'écran les fenêtres d'application et la machine écrit le code correspondant) les objets fondamentaux comme les listes déroulantes, les barres d'outils etc. sont passés dans le standard des objets élémentaires et les langages ont encore effectué une évolution. Ainsi Turbo Pascal pour Windows est devenu Delphi qui est à la fois un langage et un environnement de développement basé sur le langage pascal. SmallTalk lui, a utilisé une approche radicalement différente : conçu dés le départ pour gérer des objets (il s'agissait initialement de gérer de "vrais" objets réels comme des imprimantes, des photocpieuses) avec une interface ergonomique, il a tout de suite mis en place les concepts d'un langage objet "pur" : on commence par les objets, on finit par les programmes. Cela oblige les programmeurs classiques à changer leur mode d'écriture mais cela donne un langage très cohérent doublé d'un environnement adapté à l'écriture de classes et de méthodes... |
|
SmallTalk n'est donc pas uniquement un langage mais aussi un environnement de développement.
Nous présentons ici la version "historique" SmallTalk V pour Windows telle qu'elle fonctionnait
dès 1991 sur un PC avec un processeur 286 (si, si cela a existé !).
Un tel environnement n'a rien à envier aux EDI (environnement de développement intégré) des années 2000, sauf pour les interfaces visuelles, ce qu'on peut aisément lui pardonner vu son grand age... De plus elle fonctionne encore très bien après l'an 2000, ce qui la rend sympathique ! Lorsqu'on lance SmallTalk, une fenêtre d'interaction apparait, nommée Transcript. On peut y écrire des commentaires entre des guillemets, écrire des instructions que SmallTalk viendra exécuter.
Par exemple si on écrit dans la fenêtre de Transcript le texte 'bonjour' size et si on sélectionne ce texte à la souris, un clic sur le bouton droit permet d'éxécuter l'instruction avec "Show it". SmallTalk écrit alors 7 ce qui signifie que la longueur de la chaine 'bonjour' est 7.
La différence entre le mode "Do it" et le mode "Show it" est importante : "Do it" exécute l'instruction mais ne dit rien par défaut, "Show it" affiche la derniere expression renvoyée par l'instruction. Si par exemple on trie une liste et qu'on met le résultat dans un fichier, "Do it" pourra se contenter de tout effectuer sans affichage intempestif alors que "Show it" viendra éventuellement afficher toute la liste triée, ce qui peut encombrer la fenêtre de Transcript. On n'écrit pas de "programme" en SmallTalk mais des sous-programmes (on dit en fait une méthode plutot que sous-programme) relatifs à une classe d'objets choisis. Pour cela, après avoir réfléchi à la classe d'objets à utiliser, on utilise le gestionnaire de classe qu'on apppelle par le menu File / Browse Classes.
Une nouvelle fenêtre apparait, composée de 4 panneaux (pane en anglais). Les 3 panneaux du haut permettent de choisir les classes et les méthodes, celui du bas à écrire le texte de la méthode.
Soit la classe existe déjà (comme par exemple pour les nombres) soit on l'invente à l'aide de la commande Classes / Add Subclass.
Toutes les classes (il y en a en gros 170) ne sont pas affichées (on s'en rend compte à cause des symboles ... en fin de nom de classe). Un double-clic sur le nom de la classe affiche les sous-classes. Si on ne sait pas où est une sous-classe (par exemple pour un(e) débutant(e) il n'est pas du tout évident que la classe des nombres "Number" est une sous-classe de la classe "Magnitude"), on peut utiliser la commande Class / Find Class ou consulter une liste papier ou une page web comme celle du professeur Naugler pour avoir toute la hiérarchie des classes. Pour écrire une méthode, donc, une fois la classe choisie, on rend actif le panneau inférieur du gestionnaire de classe (qui est une fenêtre d'édition) à l'aide de la commande Methods / New Method si la méthode n'existe pas. Si la méthode existe, cliquer sur son nom affiche le texte de la méthode. SmallTalk sait se positionner sur la première méthode dont l'initiale est tapée au clavier (comme pour les Explorateurs Windows des années 2000) : ainsi, pour trouver la première méthode dont le nom commence par m on tape m dans la liste de méthodes.
La copie écran ci-dessous montre par exemple l'édition de la méthode moyenne dans la classe "Collection".
Lorsqu'on enregistre la méthode par un clic-droit et la commande Save, le nom de la méthode apparait dans le panneau supérieur gauche du gestionnaire de classes : le sous-programme fait alors partie de SmallTalk au meme titre que les autres méthodes de SmallTalk.
On notera que le texte des toutes les autres méthodes de SmallTalk (sauf quelques méthode dites "primitives") s'affiche dès qu'on clique sur son nom dans le panneau supérieur droit du gestionnaire de classes : on a donc accès au code-source de tout le langage, ce qui est un avantage énorme pour un développeur professionnel puisqu'on a la possibilité de modifier tout le langage. Pour quitter SmalTalk, on clique sur l'icone de SmallTalk dans la fenêtre de Transcript au-dessus du menu File (et oui, la CUA avec le menu Fichier / Quitter n'existait pas à l'époque) et on choisit Exit SmallTalk. Il reste alors à valider nos modifications en faisant un "backup" de l'image de l'environnement ou à les ignorer et le tour est joué.
Attention : SmallTalk garde une trace de TOUT ce qui a été fait dans le fichier change.log qui ne doit en aucun cas etre modifié (mais c'est un texte lisible qu'on peut recopier et dont on peut modifier la copie). En particulier si on édite en modifiant 5 fois une méthode, on trouvera les 5 versions de la méthode dans change.log ; cela peut-etre pratique pour revenir a une version antérieure, pour archiver les changements (afin de les commenter ultérieurement). Il existe bien sur des instructions pour compresser le log afin qu'il n'ait pas une taille monstrueuse... |
|
A idées nouvelles, langage nouveau est un aphorisme digne de
SmallTalk.
Dans un langage
"classique" on trouve des instructions comme les affectations, les tests, les boucles, puis
des appels de sous-programmes. Pas en SmallTalk.
En SmallTalk, on écrit des instructions
un peu comme dans une langue, avec la règle de grammaire :
Dans la terminologie SmallTalk, cela devient
Le couple méthode et paramètres éventuels est nommé message transmis à l'objet. Le résultat est appelé réponse de l'objet au message. Ainsi en programmation traditionnelle, on pouvait écrire
avec un sous-programme RendVisiteA et deux paramètres Pierre et Paul. En SmallTalk cela devient
où Pierre est l'objet qui reçoit le message RendVisiteA: Paul. Le calcul 2 + 3 qui n'est pas une instruction dans un langage classique devient une instruction en SmallTalk où le message + 3 est envoyé à l'objet 2, message composé de la méthode + et du paramètre 3. La lisibilité est accrue par la règle "on met le symbole : si la méthode requiert des paramètres". On est donc censé comprendre la syntaxe à utiliser pour une méthode donnée. L'affectation est la seul exception : les concepteurs de SmallTalk ont laissé le traditionnel := du Pascal au lieu d'un (joli) =:
Ainsi pour affecter à n la longueur (size en anglais) de l'objet x, on écrit
n := (x size) qui doit donc s'interpréter comme l'envoi du message
:= (x size) à l'objet
n.
SmallTalk étant un langage "pur", toute instruction doit passer par le modèle cité.
Les test devienent donc des messages passés à la condition, les boucles des messages
passés aux bornes de boucle. On doit par exemple traduire le test
On notera que le symbole point (.) sert à indiquer la fin de l'instruction. Deux difficultés se présentent alors pour apprendre SmallTalk : découvrir les 2000 et quelques méthodes déjà présentes, utiliser au mieux les classes déjà présentes. Par exemple on peut réinventer la fonction puissance dans la classe Float des nombres réels sans savoir qu'elle existe déjà (elle se nomme raisedTo:) dans la classe Number. Ou on peut triturer un tableau dans la classe Array pour avoir des valeurs uniques alors que la classes Set est conçue pour. Voici à titre d'exemples quelques instructions SmallTalk standards à connaitre
SmallTalk fournit en standard de nombreux objets dont
trois objets fondamentaux à savoir
Transcript, Prompter et Message.
Transcript permet de dialoguer en interactif avec SmallTalk,
Prompter gère les fenetres d'interrogation et Message les fenetres d'information.
On se rend compte du soin apporté au langage
SmallTalk quand on remarque comment on utilise
Prompter : pour effectuer une demande dans un langage traditionnel, on doit
utiliser une instruction "écrire" pour le texte de la question, une instruction
"lire" pour le texte de la réponse avec en principe aucun moyen de fournir une
valeur par défaut. Prompter requiert lui, en obligatoire, le texte de la question
et une valeur par défaut avec le modèle
Les exemples "Bonjour" et "Table" montrent comment utiliser ces objets.Prompter prompt: question default: valeur. |
|
Un exemple très élémentaire de méthode consiste à calculer le carré
d'un nombre. Dans la classe Number, on écrit donc la méthode carré par
carre ^self*self On notera qu'il n'est pas possible de définir la méthode carre: donc avec le symbole "deux points" car SmallTalk exigerait alors un argument pour la méthode. Avec un peu plus de lisibilité, on peut détailler en
carre " calcul du carré du nombre récepteur " | leNombre sonCarre | leNombre := self . sonCarre := leNombre * leNombre . ^sonCarre Renvoyons maintenant un nombre cadré sur plusieurs positions avec un caractère variable de remplissage. Toujours dans la classe Number, on écrit donc la méthode format: avec: soit le texte
On peut alors demander à SmallTalk d'évaluer pour obtenir3.2 format: 10 avec: '*' Passons maintenant à la conversion en majuscule du premier caractère d'une chaine. Dans la classe String, nous écrivons comme méthode initialeMajuscule soit le texte'********3.2'
Mais la conversion du premier caractère reste alors... un caractère (avec le symbole $ devant). Une vraie conversion peut se faire sur la base de la méthode asUpperCase, soitinitialeMajuscule ^((self at: 1)asUpperCase),(self copyFrom: 2 to: (self size))
Enfin, pour terminer nos exemples élémentaires, voici le comptage du nombre de lignes dans un fichier texte : dans la classe Stream, nous écrivons la méthode nombreDeLignes soit le texte
nombredeLignes | nbl | nbl := 0. [self atEnd] whileFalse: [ nbl := nbl + 1 . self nextLine . ]. " fin tant que non fin de fichier " ^nbl
|
|
Comme premier exemple un peu soutenu, voici notre bonjour de référence, avec
demande du prénom, conversion en majuscule et affichage de la date
et de l'heure. La première difficulté vient de l'endroit où écrire la
méthode. Le message réduit à la méthode bonjour doit être envoyé à un objet,
mais lequel ? A priori, cet objet n'est ni une chaine, ni un nombre, ni ...
On invente donc une classe Demo (sous-classe directe de Object). Pour cela
dans le gestionnaire de classes, on sélectionne la classe "Objects" et on
valide le menu Class / Add Subclass et on tape le mot Demo dans la case
prévue à cet effet :
Une fois le nom validé par "Ok" la classe apparait dans le panneau le plus à gauche qui donne la liste des classes :
On peut alors créer une nouvelle méthode pour cette classe avec le menu Methods / New Method et y écrire notre programme, soit le texte |
bonjour " Exemple de commentaire, lire, écrire et affectation " | pren | Cursor offset: 140 @ 150. pren := Prompter prompt: ' Bonjour. Quel est ton prénom ? ' default: ' euh ?'. pren := ' Merci. Au revoir ' , (pren asUpperCase), ' ' , (Date today) printString. Cursor offset: 140 @ 300. Menu message: pren. |
Ce texte peut bien sur etre formaté différement, par exemple comme
bonjour
" Exemple de commentaire, lire, écrire et affectation "
| pren |
Cursor offset: 140 @ 150.
pren := Prompter prompt: ' Bonjour. Quel est ton prénom ? '
default: ' euh ?'.
pren := ' Merci. Au revoir ' ,
(pren asUpperCase), ' ' ,
(Date today) printString.
Cursor offset: 140 @ 300.
Menu message: pren.
pour mieux faire ressortir les messages dans les instructions.
Pour exécuter notre méthode bonjour, il nous faut un objet de classe Demo. On peut en créer un local et lui envoyer le message bonjour avec les instructions qu'il faut exécuter ensemble en sélectionnant tout le bloc d'instructions et en faisant un "Show it" ou un "Do it". A l'exécution, on obtient la fenetre de demande du Prompter puis le message de fin envoyé par Menu Une meilleure solution pour exécuter notre méthode bonjour est de créer une variable globale. Pour cela, il faut que son nom commence par une majuscule. De plus, il faut valider la création de la variable lorsque SmallTalk pose la question de la création. Par exemple, nous créons ici la variable globale Mademo : et pour utiliser bonjour, il suffit d'évaleur avec "Do it" l'expression Mademo bonjour. |
Un deuxième exemple non trivial est celui de la table de multiplication avec
affichage dans une fenêtre autre que le Transcript.
La table de multiplication s'adresse à un entier. Nous commençons par
écrire la méthode
table dans la classe Integer soit le texte
table
| fenetre |
" table de multiplication "
fenetre := TextWindow windowLabeled: 'Table de Multiplication '
frame: ( 210 @ 30 extent: 400 @(Display extent //2)y ).
fenetre cr ;
nextPutAll: ' Table de ' ;
nextPutAll: (self printString) ;
cr ;
cr .
1 to: 10 do: [ :ifois |
fenetre
nextPutAll:(ifois printString);
nextPutAll: ' fois ' ;
nextPutAll: (self printString) ;
nextPutAll: ' = ' ;
nextPutAll:((ifois * self) printString);
cr. ] .
Menu message: 'Cliquez en gauche ici pour terminer'.
fenetre close.
^nil
puis nous écrivons la demande du nombre et l'appel de cette méthode table
dans la méthode tabmult de la classe Demo soit le texte
tabmult " Table de multiplication " | n | n := Prompter prompt: 'Donner un entier de 1 à 10 ' default: '3'. ( n asInteger) table.Après un premier essai de cette méthode tabmult, le cadrage des nombres n'est pas satisfaisant. Nous écrivons alors la méthode format dans la classe Integer soit le texte
format: unNombre
" cadre sur le nb de caractères demandés"
| s |
s := self printString.
[ (s size) > unNombre ] whileFalse: [
s := ' ', s .
]. " fin de tant que "
^s
et nous modifions le texte de table en conséquence, à savoir les lignes
nextPutAll:(ifois printString);
nextPutAll:((ifois * self) printSting);
sont remplacées par
nextPutAll:(ifois format: 5);
nextPutAll:((ifois * self) format: 5);
Lors de l'évalution de
Mademo tabmult
on obtient alors le texte de la table de multiplication
puis le message de fin à l'écran On appréciera le fait de pouvoir tester séparément les méthodes, par exemple en essayant successivement dans le Transcript les expressions
|