Nos premières classes en Smalltalk
Signature : , | Mis en ligne le : 09/06/2008
Catégorie(s) :
  • GNU/Linux Magazine
  • | Domaine :
    Commentez

    Retrouvez cet article dans : Linux Magazine 84

    Après avoir vu dans le dernier article la syntaxe, nous allons présenter ici la construction complète d’une application Smalltalk en utilisant Squeak. Le jeu Quinto servira d’illustration. Ceci nous permettra d’utiliser chacun des outils essentiels du développeur Smalltalk : inspecteur, navigateur de classes, débogueur, publication dans un dépôt Monticello... Le modèle objet de Smalltalk étant extrêmement simple, c’est la richesse de ces outils qui permet à ce langage d’exprimer toute son efficacité.

    Modèle objet de Smalltalk

    Tout comme la syntaxe que nous avons présentée dans l’article précédent, le modèle objet de Smalltalk repose sur peu de concepts qui sont appliqués de manière uniforme. On applique ici le principe du rasoir d’Occam : toute considération qui compliquerait le modèle plus que nécessaire n’est pas conservée.
    • Règle 1 : A la différence d’autres langages où les types primitifs ne sont pas des objets, tout est objet en Smalltalk, y compris les entiers et les valeurs booléennes. Les données d’un objet sont privées (il n’y a pas d’exception à cette règle). Cela veut dire qu’il n’y a pas de modificateurs d’accès privés ou publics comme en Java.
    • Règle 2 : Tout objet est l’instance d’une classe.
    • Règle 3 : Une classe (ou moule) définit le comportement des instances correspondantes au moyen de méthodes publiques et de variables d’instances privées à l’objet.
    • Règle 4 : Chaque classe hérite de la description de son comportement et de sa structure (variables d’instances) au moyen d’une seule classe. C’est donc de l’héritage simple, plus simple conceptuellement que l’héritage multiple (comme par exemple en Python).
    • Règle 5 : Les objets communiquent uniquement par envois de messages. Quand un message est reçu par un objet, la méthode correspondante est activée. La méthode à activer est recherchée dynamiquement dans la classe ou ses super-classes.
    • Règle 6 : Les classes sont organisées en arbre. La racine de cet arbre est la classe Object.
    Construire une application Smalltalk consiste donc à développer un ensemble de classes qui permettront d’instancier des objets qui communiqueront uniquement par envois de messages. L’usage veut que les noms des classes Smalltalk débutent par une lettre majuscule, alors que les instances ont des identificateurs qui débutent par une lettre minuscule. Par exemple : Date, SharedQueue...

    Création d’une classe

    Afin de nous familiariser avec la construction d’une application Squeak et ainsi de voir les différents outils essentiels, nous allons prendre comme exemple un jeu bien connu : le jeu Lights-Out, également appelé Quinto. Il s’agit d’un jeu qui se joue généralement dans une grille de dimension 5 par 5. Au départ, les cases sont toutes d’une même couleur noire. Chaque sélection d’une case à la souris permet de changer sa couleur : un premier clic permet de blanchir la case, un deuxième clic la fait revenir dans sa couleur d’origine (noir). De plus, chaque sélection d’une case produit une modification dans les cases environnantes (les 4 cases au nord, au sud, à l’est et à l’ouest) : les cases noires deviennent blanches et vice-versa. Le but du jeu est de colorier l’ensemble des cases en blanc.

    /img-articles/lm/84/art-6/fig-1.jpg

    Fig. 1 : Le plateau de jeu Quinto

    Pour construire une nouvelle classe, nous utilisons un outil essentiel fourni par l’environnement Squeak : le navigateur de classes (system browser). Pour ouvrir un nouveau navigateur de classes, sélectionnez dans le menu contextuel de fond d’écran : open ... puis class browser. Le navigateur est un outil complexe que vous apprendrez petit à petit à appréhender et à maîtriser. Il est composé de 5 grands panneaux qui permettent de naviguer de manière hiérarchique entre les classes de Squeak et les méthodes de ces classes. Voir la figure 2 ci-dessous. Comme il existe de très nombreuses classes dans un système Squeak (plus de 4000 dans le système que nous utilisons par exemple), il est nécessaire de les ranger en grandes catégories fonctionnelles : par exemple les classes représentant les nombres sont dans la catégorie Kernel-Numbers, celles concernant la manipulation des processus légers de Squeak dans la catégorie Kernel-Processes, etc.

    /img-articles/lm/84/art-6/fig-2.jpg

    Fig. 2 : Le navigateur de classes ouvert sur la classe QuintoBoard, dans la catégorie de classe Quinto

    La liste des catégories se trouve dans le premier panneau du navigateur de classes. Il est possible de la parcourir au moyen de l’ascenseur qui se trouve à droite de la liste. La sélection d’une catégorie à la souris permet de visualiser l’ensemble des classes de cette catégorie dans le deuxième panneau, juste à droite du premier. Pour notre application, nous allons créer une nouvelle catégorie pour ranger les différentes classes de notre jeu. Pour cela, faites apparaître le menu contextuel défini au niveau de cette liste de catégories (voir figure 2), puis sélectionnez dans le menu : add item, puis rentrez Quinto comme nom de catégorie. Cette nouvelle catégorie est immédiatement ajoutée en fin de liste. Si on sélectionne la nouvelle catégorie Quinto, pour l’instant il n’y a pas de classe associée. Commençons par construire la classe représentant l’état d’une case du jeu : la classe QuintoSwitch. Pour cela, on va réutiliser une classe déjà existante en Squeak, la classe Switch. Elle permet de représenter des objets qui n’ont que deux états possibles : on ou off et d’associer une action à effectuer chaque fois que l’état est on ou off. Une fois une catégorie sélectionnée, un modèle de définition de classe apparaît dans le panneau du bas (voir figure 2). Il suffit de remplacer NameOfSubClass par le nom de notre nouvelle classe, ici QuintoSwitch (attention de ne pas enlever le # devant le nom de la classe) et de remplacer Object par le nom de la super-classe ici : Switch et enfin de faire [Alt]-[s]. Une nouvelle classe QuintoSwitch apparaît dans le deuxième panneau à partir de la gauche. Il est déjà possible d’interagir avec des instances de la classe QuintoSwitch. La création d’un nouvel objet à partir de son moule (sa classe), appelé également la création d’une instance, est simple. Il suffit de s’adresser à la classe en lui envoyant un message ! Dans un Transcript, essayez les expressions qui suivent : On réutilise ici des messages déjà définis dans la classe Switch dont la classe QuintoSwitch bénéficie maintenant. Associons du code à l’action on et l’action off : chaque fois que le switch change d’état, on affiche un message dans le Transcript. Essayons Les messages correspondants doivent s’écrire dans le Transcript. Ajoutons maintenant une nouvelle méthode dans la classe QuintoSwitch : la méthode toggle, qui va permettre de changer l’état d’un switch de off à on, puis de on à off. Chaque appel de la méthode toggle, fera changer d’état le switch. Pour ajouter une méthode au moyen du navigateur de classes, il faut sélectionner la classe correspondante, puis ajouter une catégorie pour ranger la méthode. En effet, de la même façon qu’il y a des catégories de classes afin de pouvoir ranger les très nombreuses classes du système, il y a des catégories pour ranger les méthodes d’une classe. Rajoutons une catégorie state, en sélectionnant dans le menu contextuel de la liste des catégories de méthodes (3e panneau du navigateur de classes), l’item : new category, puis new... Puis enfin, sélectionnons la catégorie de méthodes state. Il apparaît un modèle de méthode dans le panneau du bas. Effacez le modèle pour le remplacer par le code suivant : Puis compilez la méthode par [Alt]-[s] (il y a génération à la volée du byte-code correspondant). La première ligne (qui devient noire après la compilation) est le nom de la méthode (ici toggle), suivie par le code qui sera exécuté lorsque l’instance recevra le message toggle. Nous pouvons maintenant utiliser la méthode toggle sur l’instance que nous avons précédemment créée : Ici s’exprime toute la puissance de Smalltalk qui permet de rajouter (ou d’enlever) des méthodes à des instances déjà existantes, ce qui permet d’avoir une approche incrémentale du développement. Expliquons maintenant le code de la méthode toggle : la première expression utilise la variable d’instance on définie dans la classe Switch qui contient l’état du switch. On calcule le non logique de la variable d’instance on et on remet le résultat dans la variable d’instance on. La deuxième expression utilise le mot clé self (un des 5 mots clés de Smalltalk). self est une variable (identique à this pour Java) qui contient l’objet courant lors de l’application de la méthode toggle. Cela permet d’accéder facilement à l’objet qui est en train d’exécuter la méthode. Ici, on applique la méthode changed sur l’objet courant. On verra plus tard que le switch est en fait le modèle d’un composant graphique, la méthode changed permet d’informer le composant graphique qui représentera le switch à l’écran.

    Inspectons un objet

    Une autre façon d’interagir avec les objets est d’utiliser un inspecteur. Il s’agit d’un outil Smalltalk qui permet de connaître comment un objet est constitué, c’est-à-dire les objets qu’il utilise et leur valeur. Pour cela, il suffit de sélectionner une expression Smalltalk quelconque dans un Transcript ou encore un navigateur de classes et, à l’aide du menu contextuel correspondant, de sélectionner inspect it au lieu de do it ou print it. Vous pouvez aussi envoyer le message inspect au receveur : q inspect. Un inspecteur s’ouvrira alors sur l’objet correspondant (voir figure 3). Comme on utilise fréquemment l’inspecteur, il existe un raccourci clavier : [Alt]-[i]. Il est également possible d’envoyer à n’importe quel objet du système le message ‘inspect’. Un inspecteur est composé de trois panneaux :
    • Une liste d’éléments à gauche : (i) self est l’objet lui-même, (ii) all inst vars permet d’avoir une vue synthétique de toutes les variables d’instance de l’objet, (iii) puis la liste des variables d’instance de l’objet. Il est bien sûr possible d’inspecter les constituants d’un objet en utilisant [Alt]-[i] dans l’inspecteur.
    • Un panneau à gauche affichant le contenu de l’élément sélectionné dans la liste précédente.
    • Un panneau en dessous dans lequel il est possible d’écrire du code. Généralement, on l’utilise pour dialoguer avec l’objet inspecté.

    /img-articles/lm/84/art-6/fig-3.jpg

    Fig. 3 : L’inspecteur ouvert sur notre instance de QuintoSwitch

    Inspectons le contenu de q : On s’aperçoit que q contient 4 variables d’instance :
    • dependents une liste d’objets dont le switch dépend, vide pour l’instant ;
    • on l’état du switch ;
    • onAction un bloc évalué lorsqu’on exécute la méthode turnOn ;
    • offAction un bloc évalué lorsqu’on exécute la méthode turnOff.
    Il existe un outil alternatif à l’inspecteur qui s’appelle " l’explorateur " qui permet de parcourir des structures d’objets très complexes. Essayez par exemple : World explore. World est l’objet représentant le bureau de Squeak.

    Construisons une interface graphique pour notre jeu

    Passons maintenant à l’interface graphique de notre jeu. Pour cela, nous allons utiliser la bibliothèque d’éléments d’interface de Squeak, appelée " Morph " Les Morphs sont des objets graphiques avec lesquels l’utilisateur peut facilement interagir pour construire des interfaces plus complexes. Nous reviendrons plus tard, dans cette série d’articles, sur les classes Morph. Pour l’instant, nous nous contenterons d’utiliser une classe déjà existante, la classe BorderedMorph représentant des Morphs avec des bords. La classe QuintoBoard que nous allons introduire maintenant est une sous-classe de BorderedMorph.

    /img-articles/lm/84/art-6/fig-4.jpg

    Fig. 4 : Exploration des objets composant le ‘World’

    La classe QuintoBoard est également définie dans la catégorie Quinto, comme pour la classe précédente. On ajoute une variable d’instance à la classe QuintoBoard : switchMatrix, qui contiendra l’ensemble des Morphs pour représenter les switchs du jeu. Pour ajouter une variable d’instance dans une classe, il faut mettre le nom de la variable dans le modèle de la classe au niveau de la ligne : instanceVariablesNames: Les variables sont énumérées entre quotes et séparées par des espaces.

    Commençons par une méthode de classe, pour créer des instances de jeu :

    Une méthode de classe, comme son nom l’indique, est la définition d’un message qui est envoyé à une classe et pas à une instance. Généralement, les messages de construction d’objets sont des méthodes de classe. Pour définir une méthode de classe dans le navigateur de classes, il faut au préalable sélectionner le bouton class et procéder comme nous avons déjà vu pour la création d’une méthode d’instance. Le bouton instance permet de revenir à la vue des méthodes d’instance.

    initializeToStandAlone est la méthode appelée pour initialiser un nouveau Morph. La méthode initializeToStandAlone définie dans la super-classe de QuintoBoard est appelée en premier pour initialiser ce qui est commun à tout Morph. Ensuite, le reste de la méthode initializeTo StandAlone consiste à initialiser la partie spécifique de notre objet, à savoir, d’une part, la présentation du composant graphique et, d’autre part, les différents switches.

    On définit une politique de présentation des switches proportionnelles, c’est-à-dire que les sous-composants seront dimensionnés lorsque le jeu sera redimensionné à la souris par l’utilisateur. On utilise des bords ronds pour le jeu et sa taille est de 200 par 200. La méthode initializeSwitches parcourt la structure de données (une matrice de dimension 2) et pour chaque case de cette matrice, construit l’élément graphique correspondant au moyen de la méthode createSwitchAt:at: On utilise ici, la méthode to:do: de parcours d’une collection, comme nous l’avons introduite dans l’article précédent. La taille de la matrice est définie par la méthode tileNumerPerSide. Cette dernière retourne tout simplement 5, qui est la taille par défaut du jeu : La méthode de création des sous-morphs createSwitchAt:at: est la plus complexe : Enfin, la méthode toggleOther: gère les modifications de la grille chaque fois que l’utilisateur renverse une case :

    Essayons notre code

    Il nous reste maintenant à tester notre code. Écrivons dans le Transcript : newStandAlone est le message de création d’un Morph, openInWorld affiche le nouveau Morph à l’écran. En exécutant ce message, il semble y avoir un problème... En effet, une boîte d’erreur apparaît, affichant le message : MessageNotUnderstood: UndefinedObject>>at:at:put: Que se passe-t-il ? Pour le savoir, utilisons un outil très puissant de Smalltalk : le débogueur. Faisons-le apparaître en cliquant sur le bouton debug de la boîte d’erreur (voir figure 5). Le débogueur permet de connaître le contexte d’exécution lors de l’apparition de l’erreur : en haut, se trouve la liste des appels des méthodes ; dans le panneau du milieu, le code Smalltalk ; en bas à droite, l’objet courant sur lequel la méthode sélectionnée est en train de se dérouler ; en bas à gauche, le contexte de la méthode (variables locales, paramètres de la méthode). La portion de code posant problème est mise en surbrillance. Il est possible d’avancer pas à pas dans le code, d’inspecter les différents objets, d’évaluer des portions de code comme dans un Transcript et même de modifier le code en cours de débogage, comme dans un navigateur de classes. Certains développeurs Smalltalk ne programment quasiment que dans le débogueur, cela leur permet d’être au plus près du contexte. Ici, dans notre application, nous nous apercevons que la matrice switchMatrix qui doit contenir l’ensemble des switches de notre application n’est pas initialisée correctement (i. e. elle est à nil). Rajoutons donc une méthode d’initialisation de cette matrice :

    /img-articles/lm/84/art-6/fig-5.jpg

    Fig. 5 : Le débogueur ouvert sur le code fautif

    Il ne faut pas oublier également d’appeler cette méthode depuis la méthode : initializeToStandAlone

    Relancez le jeu Quinto, il devrait maintenant fonctionner sans problème. Nous vous laissons à faire en exercice la détection de fin de jeu qui n’est pas réalisée.

    Travaillons à plusieurs sur du code Smalltalk

    Jusqu’à présent, nous avons travaillé seuls sur notre code. Généralement, une application un tant soit peu complexe nécessite l’interaction de plusieurs développeurs qui partagent leurs codes. Dans le monde du Logiciel libre, les développeurs utilisent des outils de gestion de partage et de version comme CVS ou maintenant SVN. Squeak utilise un principe similaire adapté aux spécificités de Smalltalk, il s’agit de Monticello. SqueakSource est un serveur Monticello géré par la communauté Squeak pour héberger des applications et librairies libres. Il permet à tout un chacun de poster et de partager son code. Nous avons créé un projet Quinto sur SqueakSource qui vous permettra de récupérer le code de cet article afin de pouvoir le tester. Allons sur la page de Quinto sur SqueakSource : http://www.squeaksource.com/Quinto.html SqueakSource offre un certain nombre d’outils utiles au développement : wiki pour la documentation, fil RSS pour le suivi des modifications, gestion des droits des développeurs, etc. Ouvrons, depuis une nouvelle image Squeak, le gestionnaire de dépôt Monticello : sélectionnez le menu contextuel de fond d’écran, puis choisissez open... puis Monticello Browser. Ajoutons un dépôt de paquetages Monticello en cliquant sur le bouton +Repository, puis saisissez dans la zone de dialogue : Cette information se trouve également sur la page du projet Quinto. Ouvrons le dépôt Quinto, en cliquant sur le bouton open du browser Monticello (voir photo d’écran ci-dessous). Une liste des paquetages Monticello apparaît. Chaque paquetage correspond à une version du logiciel. Il est possible de charger de manière sûre chaque version, de revenir à une version précédente, de comparer les différences entre versions, de fusionner des versions résultants d’un fork d’une application, de publier une nouvelle version du logiciel (soit par l’interface web, soit directement en utilisant l’outil fourni dans l’image Squeak).

    /img-articles/lm/84/art-6/fig-6.jpg

    Fig. 6 : Ouverture du dépôt Quinto à l’aide de Monticello, sur SqueakSource

    Nous ne développons pas plus pour l’instant les fonctionnalités très avancées de Monticello. Sélectionnez la dernière version publiée de Quinto et chargez-la dans l’image Squeak, en cliquant sur Load. Dans un navigateur de classes, vous pourrez avoir accès au code des classes QuintoBoard et QuintoSwitch, ainsi qu’à des implantations alternatives de ce jeu. La version que nous avons décrite ici n’est pas forcément la meilleure implantation objet, mais elle nous a permis de nous familiariser avec les différents outils de développement de Smalltalk : inspecteur, navigateur de classes, débogueur.

    En conclusion

    Les outils de Smalltalk sont riches ; nous n’avons pas la place ici d’aborder toutes les fonctionnalités. Il vous faudra essayer par vous-mêmes ! Leur uniformité (il est possible de coder dans le navigateur de classes, dans le débogueur, dans un Transcript...) et leur intégration avec le modèle objet de Smalltalk permet d’avoir une approche incrémentale du développement logiciel. C’est l’approche qui a été formalisée par l’eXtreme Programming. Ce n’est pas par hasard que des outils similaires ont été reproduits dans Eclipse, l’IDE Java de IBM. Le développement en Smalltalk peut sembler déroutant dans un premier temps : en effet, tout se passe dans l’image Smalltalk qui contient l’ensemble des classes. Il n’y a pas besoin de manipuler des fichiers notamment. Les photos d’écran de cet article ont été effectuées avec la version 3.9 de Squeak qui doit sortir prochainement. Cette nouvelle version apporte de très nombreuses corrections et de nouvelles fonctionnalités (par exemple, l’explorateur vu ci-dessus affiche les objets sous forme iconique) dont nous reparlerons. L’exemple fonctionnera également avec la version 3.8. Les développements autour de la nouvelle version 3.9 sont principalement faits en utilisant Monticello. Vous trouverez d’autres exemples d’applications simples sur le wiki de la communauté francophone (voir lien ci-dessous) : par exemple le tutoriel du compteur. Une vidéo montrant la construction de l’application Quinto est disponible ici : http://users.info.unicaen.fr/~serge/share/videos/lm84/

    Retrouvez cet article dans : Linux Magazine 84

    Vous souhaitez commenter cet article ?
    Brèves Flux RSS
    Édito : GNU/Linux Magazine N°170
    Édito : GNU/Linux Magazine Hors-Série N°71
    Édito : MISC N°72
    Édito : Open Silicium N°10
    Édito : GNU/Linux Magazine N°169
    Communication RSS Com. RSS Presse
    HACKITO ERGO SUM
    GNU/Linux Magazine, partenaire du SymfonyLive Paris
    Opensilicium, partenaire de RTS EMBEDDED
    Linux Pratique et Linux Essentiel, Partenaire de l’Open World Forum
    Gnu/Linux Magazine, Partenaire des JDEV 2013
    Rechercher un article dans notre base documentaire :
    En kiosque Flux RSS

    Le tout nouveau GNU/Linux Magazine est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau GNU/Linux Magazine HS est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Misc est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Open Silicium est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Linux Pratique est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...