Tableur

CS-108 — Série 7

Introduction

Le but de cette série est de vous familiariser avec le patron Observer en l'appliquant pour concevoir un tableur très simple. Notez que même si ce tableur est similaire à celui présenté dans les notes de cours, il est néanmoins suffisamment différent pour que cette série ne soit pas un simple exercice de copie de code.

Pour commencer, créez un nouveau projet pour cette série à partir du contenu de l'archive Zip que nous mettons à votre disposition.

Cette archive contient les classes suivantes :

  • Cell, qui représente une cellule du tableur, actuellement non observable,
  • Spreadsheet, qui constitue le programme principal,
  • Operator, un type énuméré qui définit les 4 opérateurs utilisables dans le tableur (addition, soustraction, multiplication et division),
  • ConstantOperator, qui représente une valeur constante entière placée dans une cellule du tableur.

Pour cette série, vous devrez principalement ajouter de nouvelles classes et interfaces, et modifier la classe Cell. Un tout petit ajout devra également être fait à Spreadsheet, mais il n'est pas nécessaire de comprendre son fonctionnement en détail — d'autant qu'elle utilise des lambdas, concept que vous ne connaissez pas encore.

Attention, la classe Spreadsheet utilise JavaFX, il vous faut donc l'installer sur votre ordinateur si ce n'est pas déjà fait, puis ajouter la bibliothèque au projet en suivant les instructions de notre guide sur le sujet.

La classe Spreadsheet est une application graphique qui construit un tableur très simple de 9×9 cellules. Les colonnes du tableur sont étiquetées avec les lettres de A à I, tandis que les lignes le sont avec les chiffres de 1 à 9. Une cellule est nommée en fonction de sa colonne et de sa ligne. Par exemple, la cellule en haut à gauche porte le nom A1.

Le contenu d'une cellule peut être modifié en cliquant sur elle, ce qui provoque l'apparition de son nom en bas à gauche de la fenêtre, et de son contenu actuel dans le champ textuel adjacent. En modifiant ce champ et en pressant sur la touche entrée (return), on modifie le contenu de la cellule.

Une cellule peut contenir soit :

  • une valeur constante, qui doit être un entier positif, p.ex 12, soit
  • une formule, qui peut être une addition (+), soustraction (-), multiplication (*) ou division (/) de deux cellules, p.ex. =A1+A2 pour la somme du contenu des cellules A1 et A2.

Attention, le format des formules est très restrictif et aucune espace n'est tolérée entre les noms de cellules et l'opérateur. N'oubliez pas le signe = en tête.

Dans sa version actuelle, le tableur permet bien de modifier le contenu des cellules mais les valeurs des cellules ne sont pas correctement affichées, car ces dernières ne sont pas observables. Le but de cette série est de corriger cela pour obtenir une version fonctionnelle du tableur.

Avant de poursuivre, ouvrez la classe Cell et lisez son code pour le comprendre. Vous verrez en particulier que, dans un soucis de simplicité, le contenu d'une cellule est représenté de manière uniforme par :

  • une chaîne contentString contenant la représentation textuelle du contenu de la cellule, p.ex. la chaîne 12 pour une cellule contenant 12, ou la chaîne =A1+A2 pour une cellule contenant la somme des cellules A1 et A2,
  • une liste de cellules arguments qui est soit vide pour les cellules de valeur constante, soit composée d'exactement deux cellules pour les cellules contenant une formule,
  • un opérateur binaire operator de type IntBinaryOperator qui contient l'opérateur binaire — c.-à-d. à deux arguments — permettant de calculer la valeur de la formule associée à la cellule.

Pour les cellules contenant une valeur constante, l'opérateur ignore ses deux arguments et retourne toujours la valeur de la cellule. La classe ConstantOperator représente un tel « opérateur ».

Grâce à cet « opérateur constant », il est possible de n'avoir qu'une seule classe pour les cellules, plutôt que deux comme dans les notes de cours, ce qui simplifie les choses lorsque le contenu d'une cellule change de type (de constant vers formule, ou vice versa).

Exercice 1

Le but du premier exercice est de rendre les cellules du tableur observables, afin qu'elles se mettent à jour correctement.

Pour cela, commencez par définir deux interfaces :

  • la première, nommée Subject, a pour but d'être implémentée par les classes pouvant être sujet d'observation, et contient deux méthodes : l'une pour ajouter un observateur au sujet, l'autre pour le supprimer,
  • la seconde, nommée Observer, a pour but d'être implémentée par les classes désirant en observer d'autres, et contient une seule méthode qui est appelée lorsqu'un sujet observé par l'observateur a une nouvelle valeur.

Une fois ces interfaces écrites, définissez une classe abstraite nommée AbstractSubject dont le but est de fournir des mises en œuvre par défaut des méthodes d'ajout et de suppression d'observateurs, ainsi qu'une méthode protégée permettant d'avertir tous les observateurs que le sujet a changé. Cette dernière méthode a pour but d'être appelée par les sous-classes de AbstractSubject lorsque leur contenu a changé.

A ce stade, tout est en place pour rendre les cellules observables. Pour ce faire :

  1. faites hériter la classe Cell de AbstractSubject, afin qu'elle puisse être sujet d'observation (par d'autres cellules, entre autres),
  2. faites en sorte que les instances de la classe Cell informent leurs observateurs lorsque leur valeur (retournée par getValue) change,
  3. faites implémenter l'interface Observer à Cell, afin que ses instances puissent être informées des changements de contenu des autres cellules dont elles dépendent éventuellement,
  4. faites en sorte que les instances de la classe Cell s'enregistrent bien comme observatrices auprès des cellules dont elles dépendent, et mettent au besoin leur valeur à jour lorsque celles-ci changent de valeur.

Cela fait, ouvrez la classe Spreadsheet et trouvez la ligne indiquée par un commentaire TODO. A cet endroit, ajoutez un observeur à la cellule cell pour que, lorsque sa valeur change, le texte du bouton cellButton soit changé en fonction. Ce changement peut se faire au moyen de la méthode setText que la classe Button hérite de Labeled.

Une manière de définir l'observateur consiste à en faire un enregistrement privé, imbriqué dans Spreadsheet et implémentant l'interface Observer. Ses attributs sont les objets dont la méthode update a besoin pour correctement mettre à jour le texte du bouton.

A ce stade, votre tableur devrait être fonctionnel, ce que vous pouvez vérifier en entrant des valeurs et des formules dans plusieurs cellules, et en vous assurant que les mises à jour sont correctement propagées.

Exercice 2

Entrez successivement les formules et valeurs suivantes dans votre tableur :

  1. A1=B1+C1,
  2. B1=A1+C1,
  3. C1=1.

Que se passe-t-il ? Pourquoi ? Pouvez-vous imaginer une solution à ce problème ?

Exercice 3

Entrez successivement les formules et valeurs suivantes dans votre tableur (notez qu'il est normal que C2 soit modifiée à deux reprises) :

  1. A1=10,
  2. B1=1,
  3. C2=5,
  4. C3=B1/C2,
  5. C2=A1-C1,
  6. C1=A1-B1.

Cela fait, essayez de donner les valeurs successives suivantes à A1 :

  1. A1=20,
  2. A1=30,
  3. A1=5,
  4. A1=4.

Que se passe-t-il ? Pourquoi ?

En essayant de comprendre le problème, récrivez (à la main) la formule pour la cellule C3 en essayant de la simplifier au maximum. Que constatez-vous ?