Exemple du compteur
From OFSET Wiki
Traduction de : https://www.sarit.ch/~scg/Teaching/Smalltalk/Exercises/03STExerciseCounter.pdf
Exemple du compteur
Contents |
[edit] Un compteur simple
Nous voulons implémenter un compteur simple qui implémente ce qui suit :
|counter| counter := SimpleCounter new. counter increment; increment. counter decrement. counter value = 1
[edit] Créer sa propre classe
Dans cette partie, vous allez créer votre première classe. Traditionnellement dans les environnements Smalltalk, une classe est associée à une catégorie (équivalent à un répertoire contenant les classes de votre projet). Les étapes que nous allons effectuer sont les mêmes chaque fois que vous créer une classe, tâcher donc les mémoriser. Nous allons construire une classe SimpleCounter dans une catégorie DemoCounter.
[edit] Créer une classe
Créer une classe nécessite 5 étapes. Ceci consiste essentiellement à éditer le modèle de classe fourni pour spécifier la classe que vous créez.
1. Spécification de la superclasse. Vous devez remplacer le mot NameOfSuperClass avec le nom Object. Ainsi vous spécifier la super-classe de la classe que vous spécifiez.
2. Nom de la classe. Ensuite vous devez indiquer le nom de la classe en remplacant le nom NameOfClass avec le mot SimpleCounter. Notez bien que le classe commence avec une lettre majuscule et n'enlevez pas le symbole # devant NameOfClass.
3. Spécification des variables d'instances. Vous devez ensuite indiquer le nom des variables d'instances de cette classe. Nous avons besoin ici d'une variable d'instance value. Vous l'ajoutez en remplacant les mots instVarName1 instVarName2 avec le mot value. Veuillez à conserver les ' de la chaîne de caractères.
4. Spécification des variables de classe. Comme ici, on n'a pas besoin de variables de classe, on laisse une chaîne vide (classInstanceVariableNames: ).
5. Compilation. Voila ! Nous avons maintenant une définition complète de la classe SimpleCounter. Pour la définir, il reste à la compiler. Pour cela, il faut sélectionner l'option accept dans le menu opération (click sur le bouton droit de la souris). La classe SimpleCounter est maintenant compilé et immédiatement ajouté au système.
Comme nous sommes des développeurs bien disciplinés, nous ajoutons également un commentaire à la classe SimpleCounter en cliquant sur le bouton Comment. Exemple de commentaire :
SimpleCounter is a concrete class which supports incrementing and decrementing a counter Instance Variables : value <<Integer>>
Sélectionner accept pour enregistrer le commentaire dans la classe.
Définir le protocole et les méthodes
Dans cette partie, nous allons utiliser le System Browser pour apprendre à ajouter protocoles et méthodes.
[edit] Créer et tester les méthodes
La classe que nous avons défini a une variable d'instance value. Vous devez vous rapeller qu'en Smalltalk, tout est un objet, que les variables d'instances sont privées à l'objet et que la seule façon d'interagir avec un objet est de lui envoyer des messages.
Par conséquence, il n'y a pas d'autres mécanismes pour accéder à une variable d'instances de l'extérieur d'un objet que d'envoyer un message à un objet. Ce que l'on peut faire est de définir des messages qui retournent la valeur de la variable d'instance d'une classe. De telles méthodes sont appellées accesseurs et c'est une pratique commune de toujours les définir et de les utiliser. Nous allons donc commencer par créer une méthode accesseur pour la variable d'instance value.
Rappellons nous que chaque méthode appartient à un protocole. Ces protocoles sont juste des groupes de méthodes sans sémantique au niveau du langage, mais qui apporte des informations de navigation importantes pour celui qui lira notre classe. Bien que les protocoles peuvent avoir n'importe quel nom, les développeurs Smalltalk suivent certaines conventions pour nommer ces protocoles. Si vous définissez une méthode et vous n'êtes pas sur du protocole auquel elle devrait appartenir, prenez le temps de parcourir du code existant et essayez de trouver un nom similaire.
Une remarque importante : les accesseurs peuvent défini dans les protocoles accessing ou private. Utiliser le protocole accessing lorsque un client (comme une interface) nécessite l'accès à cette donnée. Utiliser private pour indiquer clairement qu'aucun client ne devrait accéder à cet accesseur. Ceci est une pure convention. Il n'y a aucun moyen en Smalltalk d'obliger des droits d'accès comme private comme en C++ ou Java.
Question : Décider dans quel protocole vous allez mettre l'accesseur pour value. Nous allons maintenant définir la méthode d'accès à la variable d'instance value. Démarrer en sélectionnant la classe SimpleCounter dans un navigateur de classe et assurez vous que le button Instance est bien sélectionné. Créer un nouveau protocole en cliquant sur le bouton droit sur le paneau des catégories de méthodes et en choisissant New, puis en indiquant un nom. Sélectionner le nouveau nom créé. Dans le paneau du bas apparait alors un modèle de méthode. Remplacer le modèle par la méthode suivante :
value "return the current value of the value instance variable" ^value
Ceci définit une méthode value qui ne prend pas d'arguments, qui a un commentaire et qui retourne la variable d'instance value. Choisir ensuite accept dans le menu opération (bouton droit de la souris) pour compiler la méthode. Vous pouvez maintenant tester la nouvelle méthode en tapant et évaluant l'expression suivante dans un Workspace ou le Transcript ou tout éditeur de texte : SimpleCounter new value..
Cette expression commence d'abord par créer une nouvelle instance de SimpleCounter et lui envoie le message value et retourne la valeur courant de value. La valeur retournée doit être nil (la valeur par défaut pour les valeurs non initialisées). Nous créerons par la suite des instances initialisées avec une valeur raisonnable par défaut.
Une autre méthode qui est normalement utilisé en même temps que la méthode accesseur est une méthode appellée modificateur (ou encore mutable). Une telle méthode est utilisée pour changer la valeur d'une variable depuis un objet client. Par exemple, l'expression qui suit commence par créer un nouveau SimpleCounter puis ensuite fixe sa valeur à 7.
SimpleCounter new value:7
Cette méthode n'existe pas pour l'instant, donc comme exercice écrire la méthode value: telle sorte que lorsqu'on l'invoque sur une instance de SimpleCounter, la variable value prend la valeur de l'argument donné à ce message. Tester votre méthode avec l'expression ci-dessus.
Implémenter les méthodes increment et decrement dans le protocole operations.
increment self value: self value + 1
decrement self value: self value - 1
Implémenter la méthode suivante dans le protocole printing :
printOn: aStream super printOn: aStream. aStream nextPutAll: 'with value:', self value printString. aStream cr
Tester maintenant les méthodes increment et décrement en faisant attention au fait que la valeur du compteur n'est pas initialisée :
SimpleCounter new value:0; increment; value.
Notez que la méthode printOn: est utilisé lorsque l'on affiche (print it) un objet ou lorsque l'on clique sur self dans l'inspecteur.
[edit] Ajouter une méthode d'initialisation d'instance
Maintenant, nous avons besoin d'écrire une méthode d'initialisation d'instance qui donne une valeur par défaut à la variable d'instance value. Le message initialize est envoyé à toute nouvelle instance. Ceci signifie que la méthode initialize doit être défini au niveau des instances comme les méthodes increment ou decrement. La méthode initialize n'a pas de sémantique spécifique et prédéfinie; c'est juste une convention pour nommer une méthode qui est responsable entre autre de l'initialisation des variables d'instances.
Au niveau des méthodes d'instances, créons un protocole initialize-release et créons la méthode suivante (à compléter) :
initialize 'Set the initial value of the value to 0'
Maintenant créons une nouvelle instance de SimpleCounter. Est-elle bien initialisée par défaut ? Le code suivant doit fonctionner sans problème :
SimpleCounter new increment.
[edit] Une autre méthode de création d'instance
Pour voir si nous avons bien compris la différence entre méthode d'instance et de classes, créons maintanant une méthode de création d'instances nommée withValue:. Cette méthode recoit un entier comme argument et retourne une instance de SimpleCounter avec la valeur spécifiée. L'expression qui suit doit retourner 20 :
(SimpleCounter withValue:19) increment; value
Puis enfin définir une nouvelle méthode : sameValueAs: que l'on utilisera de la manière suivante :
|s1 s2| s1 := SimpleCounter withValue:5. s1 increment. s2 := SimpleCounter sameValueAs:s1. s2 increment; value.
[edit] Un point difficile
Pour créer une nouvelle instance, nous envoyons des messages (comme new ou basicNew) à une classe. Par exemple, pour créer une instance de SimpleCounter, nous envoyons new à SimpleCounter. Comme les classes sont aussi des objets en Smalltalk, elles sont instances d'autres classes qui définissent la structure et le comportement de classes. Une de ces classes qui représentent les classes comme des objets est Behavior. Parcourir la classe Behavior. La classe Behavior définit les méthodes new et basicNew qui sont responsables de la création de nouvelles instances. Si vous n'avez pas redéfini locallement à la classe SimpleCounter la méthode new, lorsque vous envoyez le message new à la classe, la méthode new exécutée est celle de la classe Behavior. Essayer de comprendre pourquoi les méthodes new et basicNew sont des méthodes d'instances de Behavior alors qu'elles sont utilisées comme des méthodes de classes de votre classe.

