6.2.2 La calculatrice

Pour qu’une calculatrice puisse livrer un résultat, elle doit recevoir deux nombres et un opérateur. Suivant l’ordre dans lequel elle reçoit ces arguments, nous parlerons d’une calculatrice postfixe7, d’une calculatrice infixe8 ou encore d’une calculatrice préfixe9.

6.2.2.1 développement du modèle de la calculatrice
Commençons en vue d’une calculatrice postfixe.

De toute façon, toute calculatrice a besoin d’un écran, nous l’appelons bufferEcran, d’un accumulateur (accu), pour garder les résultats obtenus jusqu’à maintenant, et d’un indicateur, firstTime, qui doit être true si l’on commence à écrire un nouveau nombre sur l’écran, et false quand le chiffre juste entré doit être concatené au nombre partiel déjà sur l’écran.

Voici alors la définition de la classe Calculatrice:

Model subclass: #Calculatrice  
   instanceVariableNames: ’accu bufferEcran firstTime’  
   classVariableNames: ’’  
   poolDictionaries: ’’  
   category: ’Cours-Smalltalk’
et sa méthode d’initialisation:

Calculatrice>>initialize  
   accu := 0.  
   bufferEcran := accu asString.  
   firstTime := true
Puisque les instances de la classe Touche communiquent avec la calculatrice via le couple de messages changed: et update: nous devons determiner comment réagir à l’activation d’une touche. Le plus simple est de lancer la fonctionnalité associée à la touche. Nous allons associer aux touches numériques la fonctionnalité activeNumber:, aux touches des opérateurs +, -, × et / les opérations arithmétiques correspondantes à l’aide d’une méthode activeFonction:, et à la touche C, pour Clear, la fonctionnalité activeClear:. Ce raisonnement nous donne déjà les deux méthodes suivantes pour reinitialiser la calculatrice, activeClear:, et pour traiter les touches numériques, activeNumber::

Calculatrice>>activeClear: label  
   self initialize
Calculatrice>>activeNumber: label  
   firstTime  
      ifTrue: [firstTime := false.  
         bufferEcran := label]  
      ifFalse: [bufferEcran size = 1 & ((bufferEcran at: 1)  
                  = $0)  
                      ifTrue: [bufferEcran := ’’].  
                bufferEcran := bufferEcran , label]
La deuxième méthode distingue entre le cas ou l’on commence à écrire un nombre et les autres cas. Si c’est le début de l’écriture d’un nombre, on initialise la variable d’instance bufferEcran avec le chiffre correspondant à la touche activée et transmis dans l’argument label comme une chaîne de caractère. Sinon, après avoir enlevé le ’zéro’ éventuellement affiché sur l’écran, on concatène ce chiffre au nombre déjà contenu dans la variable bufferEcran. Ainsi l’écran contiendra un nombre et, dans tous les cas, on est sûr d’être sorti de l’état où firstTime est true.

Pour l’implémentation des autres fonctionnalités nous devons prendre une décision: voulons-nous une calculatrice postfixe ou infixe? Puisque dans une calculatrice infixe l’interprétation de la touche =, par exemple, implique le lancement de l’opération arithmétique préalablement entrée, pendant que l’interprétation de la même touche dans une calculatrice postfixe ne peut qu’être le transfer du contenu de bufferEcran vers l’accumulateur, la variable d’instance accu.

Commençons alors par construire une calculatrice postfixe: elle a l’air plus simple. Néanmoins, en prévision de la construction ultérieure d’une calculatrice infixe (et pourquoi pas préfixe aussi), mettons les méthodes spécifiques à une manière de calculer dans une sous-classe spécifique. Pour notre calculatrice postfixe nous créons la sous-classe CalculatricePostfixe:

Calculatrice subclass: #CalculatricePostfixe  
   instanceVariableNames: ’’  
   classVariableNames: ’’  
   poolDictionaries: ’’  
   category: ’Cours-Smalltalk’
Dans cette sous-classe nous allons définir les deux méthodes activeEgal: et activeFonction: comme suit:
CalculatricePostfixe>>activeEgal: label  
   accu := bufferEcran asNumber.  
   firstTime := true
La méthode activeFonction: doit considerer les contenus des deux variables d’instances accu et bufferEcran comme les opérandes de l’opérateur donné par l’étiquette de la touche fonction qui a été activée. Ce qui donne:
CalculatricePostfixe>>activeFonction: label  
   accu := accu perform: label asSymbol  
                   with: bufferEcran asNumber.  
   bufferEcran := accu printString.  
   firstTime := true
Il ne nous reste qu’à écrire la méthode d’interface avec les touches, la méthode update:, qui doit activer la méthode donnée dans la variable d’instance fonction de la touche, avec comme argument l’étiquette de cette même touche. Ce qui nous donne la définition suivante:
Calculatrice>>update: aTouche  
   self perform: aTouche fonction with: aTouche label
Voilà. Nous avons terminé l’écriture du noyau d’une calculatrice postfixe, du modèle d’une telle calculatrice.
6.2.2.2 test du modèle de la calculatrice
Avant de continuer le développement de notre calculatrice, nous présentons ci-dessous, une petite méthode permettant de vérifier le bon fonctionnement du modèle:
CalculatricePostfixe class>>exemple  
   "CalculatricePostfixe exemple"  
   | aCalc t1 tPlus tEgal tCE |  
   aCalc := self new.  
   t1 := Touche new  
            label: ’1’  
            fonction: #activeNumber:  
            on: aCalc.  
   tPlus := Touche new  
            label: ’+’  
            fonction: #activeFonction:  
            on: aCalc.  
   tEgal := Touche new  
            label: ’=’  
            fonction: #activeEgal:  
            on: aCalc.  
   tCE := Touche new  
            label: ’C’  
            fonction: #activeClear:  
            on: aCalc.  
   self halt
Ici nous procédons comme dans la section 6.1.3: nous utilisons une combinaison entre le débogguer et des inspecteurs pour obtenir une première impression sur la correction du programme.

Si nous activons l’expression CalculatricePostfixe exemple nous obtenons la fenêtre déboggueur donnée dans la figure 6.13.


PIC
FIG. 6.13: La fenêtre d’exception Halt ouverte sur une calculatrice

Nous avons accès à une touche numérique, la touche t1, une touche arithmétique, la touche tPlus, la touche tEgal correspondant à la touche =, et la touche tCE pour la réinitialisation. Bref, nous avons une touche pour chacune des quatres méthodes d’interprétation possible des touches. Nous pouvons alors ouvrir un inspecteur sur la calculatrice et des inspecteurs sur chacune de ces touches pour les activer et observer leurs effets.
PIC PIC
FIG. 6.14: Deux inspecteurs sur des touches


PIC PIC
FIG. 6.15: Deux inspecteurs sur une calulatrice postfixe

La figure 6.14 montre en haut un inspecteur ouvert sur la touche t1 et en bas un autre ouvert sur la touche tPlus. L’inspecteur en haut dans la figure 6.15 montre l’état de notre calculateur postfixe après avoir
activé la touche t1, par l’évaluation de la transmission self activer dans la fenêtre d’interaction de l’inspecteur sur cette touche (cf. l’inspecteur à gauche dans la figure 6.14);

activé la touche tEgal (non montré dans les figures) de la même manière, ce qui avait comme effet que l’accumulateur accu prenait la valeur 1 (visible dans l’inspecteur gauche de la figure 6.15);

activé trois fois la touche t1, ce qui avait comme effet que la variable bufferEcran prenait la valeur ’111’ (visible dans l’inspecteur gauche de la figure 6.15).

L’inspecteur en bas dans la figure 6.15 montre l’état de notre calculatrice après avoir activé ensuite la touche d’addition tPlus (visible en bas dans la figure 6.14).

Visiblement, notre calculateur est en bonne voie: le modèle semble fonctionner correctement car l’accumulateur accu contient la somme des deux nombres que nous avons entré. Il nous reste à devélopper des vues pour cette calculatrice, pour pouvoir l’utiliser interactivement, en cliquant sur des touches et en observant les effets dans des fenêtres de cette calculatrice. Allons-y.