IPO / A3P – TP2 2425v1
Sujet

Durée : 3-4h (= TP 2.1 + 2.2)

OBJECTIFS

A l'issue de ce TP, les compétences suivantes devraient être acquises :
  1. Savoir lire un énoncé et respecter les consignes (bien respecter toutes les étapes)
  2. comprendre le concept de référence (flèche) et le principe de l'interaction entre objets
  3. corriger une erreur élémentaire de programmation après avoir analysé le message du compilateur
  4. détecter, avec BlueJ, la relation "utilise" entre deux classes
  5. écrire, sans erreur de syntaxe, une ligne d'instruction pour affecter une valeur à une variable
  6. écrire, sans erreur de syntaxe, une ligne d'instruction pour appeler une méthode avec un paramètre
  7. concevoir une suite d'instructions (à placer dans le corps d'une méthode) utilisant la récursivité pour réaliser une fonctionnalité élémentaire
  8. écrire, sans erreur de syntaxe, une ligne d'instructions permettant de créer un objet
  9. concevoir (et écrire sans erreur de syntaxe) un constructeur d'une classe en créant les attributs nécessaires et en affectant à ces attributs une valeur initiale passée en paramètre
  10. concevoir (et écrire sans erreur de syntaxe) un constructeur d'une classe qui utilise un autre constructeur de la même classe pour initialiser les attributs avec des valeurs par défaut
  11. déclarer un nouvel attribut dans une classe et savoir y accéder ensuite
  12. ajouter une méthode sans paramètre à une classe existante
  13. ajouter une méthode avec un paramètre à une classe existante
  14. ajouter des commentaires de documentation dans le code source d'une classe
    (commençant par /** et finissant par */ ) 
  15. savoir générer une documentation automatiquement (javadoc)
  16. savoir appliquer les recommandations du guide de style

1. RAPPELS

  1. Si vous n'avez pas encore complètement terminé le TP1 (alors que vous deviez le faire pendant la séance Résa1), faites-le avant de commencer le TP2 (mais appelez un intervenant si vous n'avez toujours pas fini au bout d'une demi-heure).
  2. Lors du 1er TP, vous avez vu qu'une classe (ex: Cercle) possédait des attributs (ex: aDiametre) d'un certain type (ex: int).
  3. Un constructeur permet d'initialiser les attributs, soit à des valeurs fixées dans ses instructions, soit à des valeurs qu'on lui passe en paramètres (entre les parenthèses).
  4. Un constructeur ne comporte aucun mot entre public et son nom (qui doit toujours être identique à celui de la classe). Il est appelé automatiquement à la création de chaque objet de sa classe.
  5. Une procédure (ex: vaDroite) ne retourne rien, ce qui est indiqué dans sa définition par le mot void entre public et le nom de la procédure.
  6. Mais on peut aussi définir une fonction qui retourne toujours une et une seule valeur, dont le type doit être précisé entre public et le nom de la fonction. Ses instructions se terminent toujours par un return suivi de la valeur qu'elle retourne.
  7. Lorsqu'il n'y a pas de paramètres, tant la définition que l'appel d'une fonction, d'une procédure, ou d'un constructeur se termine quand-même obligatoirement par () .
  8. Deux règles à mémoriser :
    I. Pour pouvoir utiliser les possibilités offertes par une classe, encore faut-il qu'elle ait été compilée !
    II. Pour pouvoir utiliser les possibilités offertes par un objet, encore faut-il qu'il ait été créé auparavant !
  9. Vous pourrez, dans toute la suite de l'unité, vous reporter à cet exemple complet de classe en cas de besoin (cliquez sur les pour découvrir de plus en plus de détails).
  10. Si vous n’arrivez pas à faire un exercice, regardez à la fin du sujet s’il n’y a pas un indice …

2. LECTURE / COMPILATION (15 à 20mn)

  1. Sauvegardez le fichier compilation.jar (dans Chrome, vous pouvez glisser/déposer ce lien sur la barre d'onglets) dans un sous-répertoire TP2 du répertoire consacré à cette unité, puis ouvrez-le dans BlueJ (Project > Open ZIP/JAR...).
  2. Lisez la classe MaPremiereClasse, regardez comment sont programmées/présentées les différentes méthodes. Cette classe n'a d'autre intérêt que de vous montrer un exemple assez complet de ce que l'on trouve à l'intérieur d'une classe, et de vous entraîner à comprendre les messages d'erreur du compilateur.
  3. Lisez les commentaires javadoc et retrouvez-les dans la vision « Documentation » (choisir en haut à droite); dans quel ordre les accesseurs et les modificateurs ont-ils été écrits ?
  4. Puis compilez, traduisez-comprenez le message d’erreur, corrigez, et recommencez jusqu’au succès. Si vous ne comprenez pas un mot en anglais, cherchez dans ce glossaire, et s’il n’y est pas, n’hésitez pas à en demander la traduction.
  5. Conservez ouverte la fenêtre de cette classe pour vous en inspirer dans les exercices suivants.
  6. Lorsque vous pensez avoir correctement traité les 2 erreurs qui figuraient dans ce programme, compilez si nécessaire puis cliquez-droit sur la classe en vert (MaPremiereClasseTest) et choisissez Tout tester.
    Vous devez voir en haut de la petite fenêtre qui s'affiche 2 lignes commençant par une coche verte.
    Si ce n'est pas le cas, cliquez sur la ligne en cause pour lire le message d'erreur du style "expected : <X> ; was <Y>" et si vous ne comprenez pas d'où vient le problème, appelez un intervenant.

3. CRÉATION DE MÉTHODES

Nota : le travail demandé doit être terminé, en séance ou, à défaut, hors séance.

3.1    Projet "maison" (Première partie)

Cet exercice va consister à modifier des classes d'un exemple de projet BlueJ : le projet maison

3.1.1    Préparer l'environnement de travail

Sauvegarder (dans le répertoire TP2) puis ouvrir le fichier proj_maison.jar .

3.1.2    Découvrir et essayer le projet maison

1.      Dans la fenêtre principale de Bluej,  les flèches entre classes définissent des liens de dépendance. Les flèches en tireté spécifient un lien "uses". Comme l'indiquent ces flèches, la classe Maison utilise les classes Cercle, Carre et Triangle, et ces dernières utilisent la classe Canvas. Veuillez noter que la classe Maison peut utiliser les classes Cercle, Carre, et Triangle car ces 4 fichiers .java se trouvent tout simplement dans le même répertoire/dossier.
La classe Canvas définit des utilitaires de bas niveau pour l'affichage graphique d'objets : nous ne l'étudierons pas.

2.    Après avoir compilé (si nécessaire), créer un objet Carre [ clic droit sur cette classe puis choisir new Carre() ], le rendre visible, et le déplacer de 200 pixels vers le bas ==> invoquer la méthode depVertical [ clic droit sur l'objet puis void depVertical() ] ; créer de même un objet Maison et invoquer la méthode dessine() sur cette instance.
L'image affichée illustre que l'objet de type Maison est une composition d'objets de type Cercle, Carre et Triangle. Invoquer maintenant la méthode place.
Une maison ensoleillée apparait-elle ?        
Par contre, les déplacements se font instantanément car les procédures de déplacements lents ne sont pas encore écrites correctement ; ce sera l’objet de la deuxième partie de ce tp.          

3.1.3    Modifier la classe Cercle

1.      Prendre connaissance du code source de la classe Cercle [ double-clic ].

2.      Exercice 3.1.3a : Créer dans la classe Cercle fonction suivante :

o    nom : getPosition

o    paramètre : aucun

o    valeur de retour : un entier représentant la position de l'objet codée par le calcul 1000*x + y, où x et y sont les coordonnées du cercle (on supposera avoir toujours : 0 <= coordonnées < 1000).

3.      Compiler, créer un objet de type Cercle, puis tester le bon fonctionnement de la méthode getPosition (la méthode doit apparaître grâce à un clic-droit sur l’objet). Comment savez-vous que c’est le bon résultat ? Inspectez le cercle.

4.      Exercice 3.1.3b : Créer dans la classe Cercle un deuxième constructeur :

Rappel : On notera que la classe Cercle possède ainsi deux méthodes de même nom : Cercle. Ceci ne crée pas d'ambiguïté car ces deux méthodes ont des signatures différentes. Cette possibilité qu'offre Java est appelée surcharge. Si vous avez du mal, lisez les indices tout en bas.

5.      Compiler, puis créer un objet de type Cercle à l’aide de ce nouveau constructeur. Pourquoi le cercle n’est-il pas visible ? Voir l’exercice suivant pour résoudre ce problème.


6.      Exercice 3.1.3c : Modifier les deux constructeurs (*) pour que le cercle soit automatiquement visible dès sa création. Regarder la méthode rendVisible() pour comprendre pourquoi le cercle n’était pas visible, puis appeler cette méthode plutôt que positionner aEstVisible à true.
(*) Ce n'est pas normal d'être obligé de faire cette modification deux fois, c'est à cause de la duplication de code et ce problème sera résolu à l'exercice 3.1.3d.

7.      Compiler, créer un objet Cercle des 2 façons possibles pour tester le bon fonctionnement de cette nouvelle version. Si vous avez du mal, lisez les indices tout en bas.
En travail personnel hors du tp, reportez cette amélioration dans les classes Carre et Triangle.

8.     Exercice 3.1.3d : Vous remarquerez que les deux constructeurs comportent chacun 5 instructions quasi-identiques, même si ce ne sont pas avec les mêmes valeurs. Puisque le deuxième constructeur peut mettre n'importe quelle valeur dans les 4 premiers attributs, il peut en particulier mettre celles qu'y met le premier constructeur.
Donc pour éviter de la duplication de code, il vaudrait mieux remplacer toutes les instructions du premier constructeur par un simple appel du deuxième constructeur, en lui passant des valeurs des 4 types qu'il attend.
Pour savoir quelles valeurs passer, il suffit de comprendre que le premier constructeur doit continuer de créer exactement le même cercle qu'avant !
La syntaxe pour qu'un constructeur appelle un autre constructeur (de la même classe) est très précise :
this( paramètres_attendus_par_le_constructeur_appelé );
De plus, cet appel doit forcément être la première instruction du constructeur.
Faire cette modification pour éviter l'ennemi numéro un en bonne programmation : la duplication de code !

3.1.4    Modifier la classe Maison

1.      Prendre connaissance du code source de la classe Maison.

2.     Exercice 3.1.4a : Modifier la classe Maison de façon à ce que la méthode qui rend la maison visible soit appelée automatiquement à la création de l’objet. Compiler et tester. Tout est bien placé ? Sinon, ajouter ce qu'il faut. Si vous avez du mal, lisez les indices tout en bas.

3.    Exercice 3.1.4b : Modifier la classe Maison de façon à ajouter comme 5ème composant du dessin un deuxième soleil. Faites-en sorte que seule la couleur (pas noire !) soit différente par rapport au premier soleil. Compiler et tester. Ce nouveau soleil est-il bien visible ? Sinon, corriger.
metNoirEtBlanc() et metCouleur() fonctionnent-elles toujours ?  Sinon, corriger et tester.

4.      Exercice 3.1.4c : Créer dans la classe Maison la méthode suivante
      sans modifier la classe Cercle ,  puis compiler et tester.

5.      Exercice 3.1.4d : Modifier la classe Maison de façon à ne pas avoir à répéter 2 fois à peu près la même chose dans getPositionsDeuxSoleils(). Pour cela, créer une nouvelle fonction getPositionSoleil() (publique ou privée ?), retournant une String et acceptant un Cercle en paramètre, et ne retournant la position que de ce cercle. Ensuite, getPositionsDeuxSoleils() n’a qu’à appeler getPositionSoleil() 2 fois. Compiler et tester. Si vous avez du mal, lisez les indices tout en bas.

6.      Exercice 3.1.4e : Modifier la méthode getPositionSoleil() pour qu’elle accepte en plus un paramètre String contenant le nom (que vous avez choisi) du cercle dont elle retourne la position ; la String retournée commencera donc par "leNom : ". Modifier la méthode getPositionsDeuxSoleils() en conséquence. Compiler et tester.

3.1.5    Générer automatiquement la documentation

1.  Exercice 3.1.5a : Ajouter des commentaires de documentation aux méthodes que vous avez développées (dans les classes Cercle, Carre, Triangle et Maison) pour qu'elles soient renseignées au moins au même niveau que les autres. Regarder pour chaque classe sa « Documentation » (Interface), puis prendre connaissance de l'arborescence des fichiers générés dans le sous-dossier doc de votre projet et, ouvrir le fichier package-summary.html dans un navigateur.

2.      Exercice 3.1.5b :  Prendre connaissance de l'arborescence des fichiers générée dans le répertoire proj_maison et, sous navigateur, consulter le fichier package-summary.html. Voyez-vous toutes les classes ?

4.        Pour remédier à ce problème, [fenêtre principale de BlueJ, menu Outils, choix Générer la documentation du projet]. Attendre quelques secondes le message de fin tout en bas de la fenêtre BlueJ, puis recharger la page et vérifier le résultat en navigant parmi toutes les classes du projet.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

3.2    Deuxième partie

3.2.0    D'abord terminer la première partie

3.2.1    Modifier les méthodes de déplacement lent (par récursivité)

1.      Dans la méthode depLentVertical de la classe Carre, supprimer l'appel à depVertical() qui déplace d'un seul coup de plusieurs pixels, et provoquer un déplacement lent en utilisant un appel récursif de cette méthode avec un appel de depVertical d’un seul pixel à chaque fois          
(se déplacer lentement de N pixels revient à se déplacer "rapidement" d'un pixel puis à se déplacer lentement de N-1 pixels ; mais au fait, quand le carré va-t-il arrêter de se déplacer ?).       
Pour en savoir plus sur la récursivité, lisez
ce mini-cours.
 
a) Essayez d’abord avec pDistance toujours positive, sans toucher aux lignes en commentaire. Compiler, tester. Si vous avez du mal, lisez les indices tout en bas.

b) Essayez ensuite de tenir compte de la bonne direction (utiliser vDelta en décommentant les premières lignes des méthodes de déplacements lents). Compiler, tester.      

Si vous obtenez une StackOverflowError ou si la forme clignote indefiniment, c’est que la récursion ne s’arrête jamais.         
[cliquez en bas à droite pour arrêter l’exécution]

2.      Reportez vos instructions dans depLentHorizontal en adaptant les noms. Compiler. Tester.

3.      Reportez ces modifications dans les classes Cercle et Triangle. Compiler. Tester. Maison toujours ensoleillée ?   

AIDE : Il est possible d’afficher les valeurs de vos variables avec System.out.println(variable);   

3.2.2    Améliorer la documentation

1.      Visualisez le « styleguide » de l'outil javadoc :

https://www.oracle.com/technetwork/java/javase/documentation/index-137868.html#tag .

2.      Lisez les parties consacrées aux « tags » @author, @version, @param, @return, et @see . Lisez le reste en travail personnel.

3.      Exercice 3.2.2a : Ajoutez/complétez (si nécessaire) les commentaires javadoc en incorporant des @author et un @version dans chaque classe, ainsi que le @return et les @param dans chaque méthode. Regénérez la documentation et vérifiez les informations qui y figurent désormais.

3.2.3    Terminer l'exercice

Regénérez la javadoc puis sauvegardez le projet Maison [menu Project, choix Save] et partagez les fichiers avec votre voisin. Un peu fastidieux, non ?  Alors essayez plutôt [menu Project, choix Create Jar File.../Exporter...] puis partagez uniquement le fichier .jar ainsi créé. Le destinataire n’aura plus qu’à utiliser [menu Project, choix Open ZIP/JAR...] et à désigner le fichier .jar.

Fermer le projet maison [menu Project, choix Close] , voire BlueJ.     


3.2.4    Style guide

Lire une première fois le guide de style (en anglais) qu’il vous faudra appliquer dans toute l’unité, et pourquoi pas dans la suite de vos études ; sauter certaines notions qui n’ont pas encore été vues.


4. Pour s'entraîner à la récursivité (obligatoire)

Écrivez dans une nouvelle classe Vide (complètement vide), la fonction factorielle. Pour cela, 3 lignes peuvent suffire :
signature
{
  cas particulier (condition d'arrêt)
  cas général
}
  1. D'abord écrire la signature :
    • Doit-elle avoir 0, 1, ou 2 paramètres ?
    • De quel type ?
    • Doit-elle retourner ou pas une valeur ?
    • De quel type ?
  2. Ensuite, le corps : la formule de récurrence est donnée en indice si vous ne la retrouvez pas.
  3. Enfin, il faut l'essayer : omment peut-on savoir que la valeur obtenue est crédible ?
    • Essayons 20! : un pb ? Comment peut-on obtenir un résultat négatif en multipliant des nombres positifs ? (*)
    • Soyons plus raisonnables, essayons 14! : ce résultat est-il crédible ? Pourquoi ?
    • Finalement, quelle est la plus grande factorielle qu'on peut calculer avec cette fonction ?
  4. Si on utilise un type entier 64 bits (long) au lieu de 32 (int), jusqu'où pourra-t-on aller ?
    Réessayez 20! pour vous donner confiance. Et 21! ?
    Pour votre information, en Java il existe deux autres types entiers : short (16 bits) et byte (8 bits). Quels sont le plus petit entier et le plus grand entier représentables avec un byte ?
  5. Au fait, comment a-t-on pu créer un objet de la classe Vide alors qu'on n'y a écrit aucun constructeur ? D'ailleurs qu'aurait-il eu à initialiser ? Y en aurait-il un par défaut ?
    Et est-il très logique de devoir créer un objet juste pour appeler la fonction factorielle ? Ne pourrait-on pas appeler cette fonction directement sur la classe Vide ?
    C'est ce que nous verrons bientôt, avec la notion de méthode de classe.

(*) Pour en savoir plus sur la représentation en machine des nombres entiers, vous pouvez lire ça.


5. Fin du TP

·     Avez-vous bien fait TOUS les exercices, dans TOUTES leurs étapes ?

·     Avez-vous compris TOUT ce que vous avez vu ? Sinon, demandez à un intervenant.

·     Avez-vous expérimenté le CodePad ?  (Essayez :  int i=12;   i*2   etc…)

·     Si vous avez fini avant l’heure prévue, demandez aux intervenants si vous pouvez partir avant la fin du TP ou bien s’ils considèrent que vous avez encore du travail à faire.

·     Sinon, terminez tout en travail personnel avant le prochain tp.


6. Indices pour ce TP

1)  Pour faire l'exercice 3.1.3b :

·     Dupliquez le constructeur existant. Ajoutez-lui les paramètres indiqués dans l'énoncé. Servez-vous des paramètres dans les instructions. Ça compile ?

·     Pour le 6., remplacez les 5 instructions du premier constructeur par this(); et passez-lui dans les parenthèses les bonnes valeurs pour qu'il crée toujours ce cercle bleu de diamètre 30 et de coordonnées (20,60).

2)  Pour faire l'exercice 3.1.3c :

·     Ne modifiez que le deuxième constructeur (puisque le premier l'appelle).

·     N'utilisez pas 2 instructions lorsqu'une seule suffit !

3)  Pour faire l'exercice 3.1.4a :

·     Cherchez tous les endroits du code où apparaît aSoleil, dupliquez chaque instruction, et adaptez-la pour aSoleil2.

4)  Pour écrire la méthode getPositionsDeuxSoleils :

·     N’a-t-elle bien aucun paramètre (elle n’a besoin d’aucune information supplémentaire) ?

·     Quelle fonction (de la classe Cercle) retourne la position du cercle ?

·     Comment extraire d’un nombre de la forme 20060 les coordonnées 20 et 60 (voir aide dans le sujet) ?

·     Construire la chaîne de caractères bout après bout en utilisant un + avant chaque bout (de type String ou int) supplémentaire.

5)  Pour écrire la méthode getPositionSoleil :

·     Relisez l’énoncé pour savoir quel type de valeur cette fonction retourne, et quel est le type de son paramètre.

·     Reprenez la moitié de ce que vous avez écrit 2 fois dans la méthode getPositionsDeuxSoleils, mais en appliquant les appels de méthode au cercle passé en paramètre (dans getPositionSoleil) plutôt qu’à un cercle précis (comme this.aSoleil2 par exemple).

·     Il ne reste plus qu’à remplacer la majeure partie de getPositionsDeuxSoleils par 2 appels à getPositionSoleil, des deux côtés de la chaîne " | " qui sépare les 2 soleils.

6)  Pour écrire les méthodes de déplacement lent (par exemple vertical) par récursivité :

·     Respectez pas à pas les étapes proposées dans l’énoncé ; quand on parle d’un déplacement « normal » ou « rapide », on désigne juste un appel à depVertical.

·     N’oubliez ni le test d’arrêt (quand il n’y a aucun pixel à parcourir, que faut-il faire ?), ni de modifier le paramètre (pour que la distance restant à parcourir diminue à chaque fois).

·     Lorsque cela fonctionne vers le bas, intéressez-vous à la variable vDelta (en commentaire) ; au lieu d’enlever 1 au paramètre à chaque fois, ne vaudrait-il pas mieux enlever vDelta (dont la valeur change selon le sens de déplacement souhaité) pour que cela fonctionne aussi vers le haut ?

7)  Pour écrire la fonction factorielle :

·     n! = n x (n-1)! et par convention 0! = 1 (ne pas s'occuper des entiers négatifs)