Poo td01
Poo td01
Septembre 2011
Mise à Niveau
V Interfaces Graphiques 14
1
Rappel : il est fortement conseillé de consulter la documentation en ligne sur http ://java.sun.com/.
I Mise en forme
Exercice 1.
Prise en main d’Eclipse
1. Lancer Eclipse.
2. Sélection du répertoire de travail
Dans la 1re fenêtre, sélectionner un répertoire. Ce dossier sera votre répertoire de travail où seront
stockés les sources (.java) et les fichiers compilés (.class) de tous vos programmes.
3. Premier projet
Créer un nouveau projet (File ¿ New ¿ Project).
1re fenêtre : Select a wizard, sélectionner Java project
2e fenêtre : donner un nom au projet. Vérifier que la version de java utilisée est bien la version 1.5
(partie JRE ) puis Finish
4. Première classe
Dans la partie de droite (Package Explorer), sélectionner le projet puis créer une nouvelle classe
(clic droit, New ¿ Class)
Donner un nom à la classe : Factorielle et vérifier que la case public static void main (String[]
args) est bien cochée.
Votre première classe est créée !
Exercice 2.
Un premier programme
1. Compléter la classe Factorielle que vous venez de créer : Factorielle.java
2. Sauver cette classe, et regarder dans l’onglet Problems en bas. Eclipse permet de compiler le code à
la volée (c.-à-d. au fur et à mesure que le code est saisi) et affiche les résultats de la compilation
dans cet onglet. Il est possible, en faisant un clic droit sur un message d’erreur, d’obtenir plus
d’informations sur celle-ci et, notamment, des suggestions pour la corriger.
3. Corriger ce programme pour obtenir l’affichage correct (c’est-à-dire : factorielle de 5 : 120 )
4. Pour faire tourner le programme : clic droit sur Factorielle dans le Package Explorer puis : Run
as ¿ Java application
2
short x = 5;
short y = 15;
x = x + y;
Exercice 5.
Intérêt des piles
On souhaite écrire un programme qui teste si les délimiteurs présents dans une chaine sont bien
utilisés.
Les délimiteurs que l’on considère sont : {}, [] et (). Une chaine est bien formée si chaque délimiteur
ouvrant est fermé par son délimteur fermant . Par exemple ( doit être fermé par ). Il faut
également que les délimiteurs soient fermés dans l’ordre : les délimiteurs ouverts en premier doivent
être fermés en dernier. Ces règles sont identiques à celle utilisées dans un programme Java.
Par exemple, les expressions suivantes sont correctement formées :
c[d]
a{b[c]d}e
mais pas celles-ci :
a{b(c]d}e ( fermé par ]
a[b{c}d]e} } n’est pas ouvert
a{b(c) { n’est pas fermé
3
1. Écrivez une méthode non-récursive qui permet de tester si une expression donnée est correctement
formée ou non. Cette méthode s’appelera check et devra être définie dans une classe nommée
Parsing.
Remarque : La bibliothèque standard de Java définit une classe Stack qui implémente une pile. Pour
l’utiliser, il suffit :
– d’inclure la bibliothèque en ajoutant au début du programme la ligne : import java.util.Stack ;
– de créer une instance : Stack¡Type¿ s = new Stack¡Type¿() ; où Type est le type des données que
l’on souhaite stocker dans la pile 1 .
– de manipuler la pile à l’aide des méthodes push, pop et isEmpty.
Exercice 6.
Vérification d’adresse IP Une adresse ip est définie par quatre nombres compris entre 0 et 255, séparés
par des points. Par exemple : 212.85.150.134. L’objectif de cet exercice est de vérifier si une chaı̂ne de
caractères donnée définie bien une adresse ip. Pour cela il faut vérifier que :
– elle comporte 3 points ;
– elle commence et se termine par un nombre (.123. n’est pas une adresse ip) ;
– les caractères situés entre les points sont des chiffres compris entre 0 et 255.
Compléter le code se trouvant sur le site du cours pour que celui-ci affiche ”ip valide” quand la chaine
ip est une adresse ip valdie et ”ip non valide” dans le cas contraire.
Les exemples suivant permettent de s’assurer que le code traite bien tous les cas :
”127.0.0.1” ”127.231.1.1”
”1.2.3.4” ”12.2.3”
”12.3.213.123.123” ”1231.12.2.3”
”.1.2.3” ”1.2.3.”
”1.2..3”
On pourra utiliser les instructions suivantes :
– int Integer.parseInt(String s) pour convertir une chaı̂ne de caractères en nombre (si et seulement si
la chaı̂ne de caractères correspond à un nombre) ;
– bool isNumeric(String s) pour vérifier si chaı̂ne de caractère représente un nombre (ce code sera
expliqué lors du cours prochain) :
– la méthode split de la classe String dont le fonctionnement est décrit ci-dessous :
public class ExempleSplit {
}
La sortie du programme précédent est :
1. Stack est un type paramétré, notion spécifique à Java que nous détaillerons à la fin du semestre.
4
Bonjour
les
amis
2. De plus, n’y-a-t-il pas redondance dans le code des méthodes distance. Quelle solution proposez-
vous ?
3. Que doit-on faire pour pouvoir tester l’égalité stricte entre deux points ?
4. Comment faire pour introduire la notion de précision dans l’égalité entre deux points ? Comment
s’assurer que la précision soit la même pour tous les points ?
5. Un lecteur impertinent pourrait faire remarquer, qu’il existe déjà une classe Point dans l’API
standard. Comment être sûr de laquelle est utilisée ?
5
On souhaite définir une classe Complexe pour manipuler facilement des nombres complexes.
1. Définir une classe Complexe comprenant deux champs de type double, qui représentent respective-
ment la partie réelle et la partie imaginaire d’un nombre complexe.
2. Définir un constructeur qui permette de construire un nombre complexe :
public Complexe(double r, double i) {
...
}
3. Écrire la méthode toString qui affiche un nombre complexe sous la forme : a + i * b.
public String toString() {
...
}
4. Créer un nouveau fichier Test.java. Tester le constructeur et la méthode d’affichage depuis la
méthode main de ce fichier, en construisant différents nombres (penser à tester les cas particu-
liers).
5. Définir des fonctions pour l’addition et la multiplication de deux nombres complexes. Ces deux
fonctions doivent prendre en argument un Complexe et doivent retourner un nouveau Complexe qui
est le résultat de l’opération sur le nombre argument et le nombre sur lequel est appelée l’opération.
public Complexe ajouter(Complexe c) {
...
}
public Complexe multiplier(Complexe c) {
...
}
6. Comment tester l’égalité entre deux complexes ? Ecrire la méthode appropriée.
6
Exercice 11. Les formes géométriques
L’objectif est de réaliser un éditeur rudimentaire de dessins à base de formes géométriques. On souhaite
disposer d’un package formesgéométriques regroupant les classes permettant de manipuler des formes
géométriques bidimensionnelles simples. Nous ne traiterons ici que les formes de types : Cercle, Rectangle,
et Carré.
Etant donnée, l’application visée, les contraintes suivantes sont également à satisfaire :
– Chaque forme possède un centre de gravité qui est une instance de la classe java.awt.Point ainsi
qu’une couleur qui est une instance de la classe java.awt.Color. Ces deux classes sont à réutiliser
via une composition.
– Un objet de type Cercle est caractérisé par son centre de gravité et son rayon.
– Un objet de type Rectangle est caractérisé par son centre de gravité, sa largeur et sa hauteur.
– Toute forme géométrique doit posséder les méthodes suivantes :
– translation, prenant en paramètre deux nombres exprimant le déplacement horizontal et vertical.
– homothetie, prenant en paramètre un nombre représentant le ratio de l’homothétie.
– Toute forme est capable de donner sa représentation sous la forme d’une chaı̂ne de caractères
contenant le nom de sa classe et la description textuelle de chacun de ses attributs. Par exemple,
la chaı̂ne de caractères produite pour un objet de classe Cercle est :
[Cercle
[centre de gravité : x=10 , y=4]
[rayon : 20]
[couleur : r=82 , g=255 , b=0]
]
– Toute forme possède un constructeur prenant en paramètre la couleur et initialisant les autres
attributs avec des valeurs par défaut de votre choix.
– La classe Cercle possède un constructeur prenant la couleur, le centre et le rayon comme paramètres.
– La classe Rectangle possède un constructeur prenant la couleur, le centre, la largeur et la hauteur
comme paramètres.
1. Dessiner la hiérarchie des classes de ce package.
2. En prenant exemple sur la réutilisation de la classe java.awt.Point pour réaliser la classe Cercle,
expliquez quelle est la différence entre l’agrégation et la composition. Par la suite nous utiliserons
la composition pour l’attribut centre.
3. Expliquez par un exemple le principe du polymorphisme (vous pourrez prendre exemple sur les
formes géométriques).
4. Ecrivez le code java des classes Forme et Rectangle. Apportez un soin particulier à l’encapsulation
et à la factorisation du code.
5. On souhaite ajouter la classe Carré comme spécialisation de la classe Rectangle. Cette nouvelle
classe doit répondre aux contraintes énoncées précédemment et ne doit pas pouvoir être dérivée.
Ecrivez le code de la classe Carré et prévoir en outre un constructeur prenant la couleur, le centre
et la longueur comme paramètres.
6. Ecrivez la classe Cercle.
On souhaite maintenant pouvoir composer des listes de formes géométriques pour opérer des regrou-
pements d’objets.
7. Créez une classe VectorFormes, un Vector qui ne peut contenir que des formes géométriques.
8. Ajoutez les méthodes translation et homothetie qui déplacent ou modifient la taille de l’ensemble
des objets de la liste. Ajoutez également la méthode toString() .
7
9. Redéfinissez la méthode add de la classe VectorFormes pour qu’un objet n’ayant pas de centre
défini soit placé au milieu des autres formes de la liste (en utilisant la moyenne ou la médiane par
exemple).
10. Ecrivez maintenant un main permettant de manipuler des formes et des listes de formes.
11. Quelles modifications doit-on apporter au code pour imposer que VectorFormes ne contienne que
des formes géométriques du même type, ce type étant paramétrable ?
8
Figure 1 – Damier et echiquier
2. Écrire la classe PieceDame qui décrit une pièce pour le jeu de dame. Une pièce a une couleur (noir
ou blanc) et un type (pion ou dame). Dans un souci de lisibilité, on pourra utiliser des constantes de
classe avec un nom explicite plutôt que des constantes entières non explicites (regarder les constantes
NOIR et BLANC de l’interface Piece par exemple).
3. Spécialiser le type Plateau en PlateauDeDame. En particulier le constructeur initialisera le plateau
en configuration de début de partie (comme sur la figure 1). Profiter du main pour tester la création
et l’affichage du damier.
4. Créer la classe JoueurDame (dont un attribut sera le plateau de dames) qui possède la déclaration
des méthodes suivantes :
– une méthode qui retourne vrai s’il abandonne, faux sinon
– une méthode pour jouer un coup.
Les méthodes ne doivent pas être implémentées. Ce sont des sous-classes de JoueurDame qui seront
instanciées. Comment doit donc être déclarée la classe ?
5. On souhaite créer une classe JoueurDameMauvaisJoueur qui abandonne dès qu’il a 2 dames de
moins que son adversaire et/ou 4 pièces de moins. Modifier la classe PlateauDeDame pour connaı̂tre
le nombre de dames et de pions d’un joueur. Implémenter dans JoueurDameMauvaisJoueur la
méthode d’abandon mais pas celle pour jouer un coup.
6. Ajouter une méthode à PlateauDeDame qui déplace une pièce d’une case à l’autre (les cases de
départ et d’arrivée seront données en argument) si et seulement si la case d’arrivée ne contient rien
et si le déplacement respecte la règle des dames (les cases doivent se situer sur la même diagonale).
La case de départ sera mise à null. La case d’arrivée pointera vers la pièce déplacée. La méthode
renverra un boolean indiquant si le déplacement a été effectué.
7. Créer une classe JoueurDameAgressif qui fait l’hypothèse qu’il est “en bas” du damier. Il n’aban-
donne que lorsqu’il n’a plus de pièce. Pour jouer il cherche parmi ses pièces si il peut prendre (càd
il y a un pion adverse devant et une case vide derrière). S’il n’y a aucune pièce à prendre, le joueur
9
Figure 2 – Exemple de prise de pièce
avance le pion le plus en recul (celui le plus en bas possible) ... Toujours en diagonale et de 1 case.
On ne tient pas compte des dames.
8. Spécialiser la classe JoueurDameMauvaisJoueur pour créer un mauvais joueur qui fait l’hypothèse
qu’il est “en haut” du damier et qui fait n’importe quoi (à vous de choisir l’implémentation de la
méthode pour jouer de la classe abstraite JoueurDame).
9. Créer une partie et faire s’affronter un mauvais joueur et un joueur agressif.
10
Exercice 16.
On souhaite représenter des nupplet. Par exemple un Doublet d’entiers est un nupplet pour n=2, et
constitue un couple d’entiers. Pour qu’un nupplet soit manipulable, il est souhaitable de pouvoir calculer
la somme et le produit de ses n composantes, et de prévoir une méthode add qui prend en argument un
autre nupplet et retourne un nouveau nupplet qui est la somme des deux.
1. Quel mécanisme objet proposez-vous pour modéliser un nupplet d’entiers, sachant qu’on spécialisera
par la suite ce nupplet en fonction de la valeur de n souhaitée ? Justifiez votre choix et proposez
une réalisation en Java.
2. Implémenter une classe Doublet d’entiers (pensez à écrire une méthode main pour tester chaque
méthode de votre classe et n’oubliez pas la méthode toString).
3. Implémenter une classe Triplet d’entiers en réutilisant au maximum ce que vous avez déjà fait.
11
1. commencer par en écrire la méthode main et toujours se placer du point du vue du moteur de jeu
(ou plus généralement de l’application) ;
2. respecter le fait que le moteur ne doit ni intervenir sur l’interface utilisateur ni induire de contraintes
la concernant ;
3. ainsi, le main peut s’écrire indépendament de l’implémentation des joueurs en interagissant uni-
quement avec une interface Joueur.
Regardez ensuite l’exemple de méthode main dans la classe PartieMorpionV0 : cette méthode assure
simplement le déroulement du jeu. Le fait que le joueur soit humain ou non n’a pas d’influence pas sur
son déroulement. De plus, le mode d’interaction utilisé (par exemple, console, interface graphique, réseau,
SMS, ...) par le joueur humain est transparent pour le moteur. Cette méthode main suppose donc que
la classe implémentant une interface Joueur est autonome et qu’elle gère elle-même l’interaction avec
l’utilisateur ou l’intelligence artificielle.
Voici la description des différentes classes comprises dans l’archive :
– La classe Jeu regroupe toutes les informations nécessaires au bon déroulement de la partie, notam-
ment : les deux joueurs, l’état du plateau et le joueur en train de jouer.
– L’interface Joueur représente un joueur. Du point de vue du moteur de jeu, un Joueur interagit
avec l’utilisateur et transmet les informations du Jeu au Joueur et vice versa. Un Joueur est défini
par un nom, sa marque (typiquement ’X’ ou ’O’) et une méthode joue(Jeu jeu).
– La classe abstraite JoueurGeneric propose un début d’implémentation de l’interface Joueur.
– La classe JoueurMachine représente un Joueur doté d’une intelligence artificielle. Cette classe
propose une implémentation de la méthode joue.
– La classe PartieMorpionv0 permet de gérer une partie entre 2 instances de JoueurMachine.
Par la suite, vous allez développer le package morpion.console qui contiendra les classes nécessaires à
un jeu en console.
1. On veut permettre à un utilisateur de jouer contre un joueur artificiel. Pour cela, proposez une
implémentation de JoueurHumain, dans laquelle l’interaction avec l’utilisateur se fait via la console.
Ceci nécessite de demander à l’utilisateur d’entrer un couple de coordonnées correspondant à l’en-
droit où il souhaite placer son pion. Par exemple, l’humain (c’est-à-dire vous) entre ”0 2” pour
placer un pion sur la ligne 0 et la colonne 2 (il faudra bien sûr vérifier les valeurs saisies). Voici un
exemple de l’échange :
C’est au tour de IA :(Halbert,O)
---
|?|?|?|
|?|?|?|
|?|?|?|
---
<<< 1 1
C’est au tour de Humain :(Robert,X)
---
|?|?|?|
|?|O|?|
|?|?|?|
---
>>> 0 3
12
>>> 0 2
2. PartieMorpionv0 joue une partie entre 2 joueurs artificiels. Pour tester votre joueur humain, créez
une nouvelle classe PartieMorpionv1.
13
Ce dialogue est implémenté dans la méthode continuerOuQuitter de la classe Jeu, qui peut donc faire
appel à la méthode public static void sauveJeu(Jeu j).
1. Compléter les méthodes public static void sauveJeu(Jeu j) et public static void chargeJeu(Jeu j) de
la classe IOMorpion afin de pouvoir respectivement sauvegarder un jeu et charger un jeu.
2. Afin de tester votre programme, modifiez le main de la classe PartieMorpionv1 afin qu’il soit possible
de sauvegarder et quitter une partie en cours. Jouez un peu en utilisant la classe PartieMorpionv1
(mais pas trop longtemps tout de même !). Essayez notamment la commande quit qui entraı̂ne la
sauvegarde du jeu. Puis écrivez la classe PartieCommencee qui redémarre une partie à partir d’une
sauvegarde.
3. On souhaite maintenant introduire un peu d’aléatoire lorsqu’une partie sauvegardée est reprise : si
le plateau contient autant de jetons des deux joueurs (autant de ’X’ que de ’O’), alors le joueur qui
commence est tiré au hasard. Sinon, c’est bien évidemment le joueur qui a le moins de jetons placés
qui (re)commence. Modifiez le processus de sérialisation pour remplir ces spécifications.
V Interfaces Graphiques
L’objectif de ce TD est de vous familiariser avec les principaux composants graphiques des librairies
SWING, afin de pouvoir concevoir des interfaces graphiques en JAVA. Etant donné la richesse des librai-
ries, il est impossible de tout connaı̂tre par coeur. Ce travail est donc aussi l’occasion d’apprendre à se
familiariser avec la documentation de java. Ce TD pourra s’étendre sur celui de la semaine prochaine.
http://java.sun.com/javase/6/docs/api/
La page d’accueil de la documentation donne un certain nombre de pointeurs vers des documents
utiles mais ce qui nous intéresse le plus ici est le lien appellé Java Platform API Specification, qui
donne accès à la documentation de l’ensemble des classes existantes du jdk. Cette documentation a été
générée automatiquement à l’aide de javadoc. Cet outil est capable d’exploiter un format spécial de
commentaires dans des sources java, pour générer automatiquement une documentation au format html,
consultable à l’aide de n’importe quel navigateur. De plus, pensez à utiliser les tutoriaux de sun qui vous
guident sur les choix à faire parmis les composants graphiques et et donnent des exemples d’utilisation.
1. Familiarisez vous avec l’organisation de la doc.
2. Retrouver les pages décrivant les classes JFrame, JPanel et JButton.
14
3. Pour chacune de ces classes, trouver à quel package elles appartiennent, quels sont leurs construc-
teurs, de quelles classes elles dérivent (i.e. leur position dans la hierarchie des classes) et les méthodes
qui leurs sont applicables (notamment par héritage). Après une lecture attentive de la documenta-
tion des 3 classes (JFrame, JPanel et JButton), vous devez avoir compris le fonctionnement global
de ces classes. Sinon posez des questions.
15
Exercice 25. Ajoutons le clavier
Pour réaliser le clavier de la calculette, nous allons utiliser des composants de type JButton. Ces
composants étant utilisés pour le contrôle de l’interface, il est nécessaire de les déclarer comme attributs
de la classe IGCalculette.
1. Creer un nouvel attribut pour l’interface graphique, correspondant à un tableau boutons pour le
clavier (nb : on pourra de façon utile créer des constantes de classe pour référencer les boutons qui
ne correspondent pas à des chiffres par des entiers).
2. Lors de l’initialisation de l’interface, créer les 16 boutons , chacun d’entre étant étiqueté par le
symbole approprié, et disposer le tout de façon adéquate sur le pannel correspondant au clavier.
Quel est le Layout manager qui vous semble le plus approprié ?
Implémenter une classe interne pour un tel gestionnaire d’événements et associer ce gestionnaire à
l’ensemble des boutons du clavier.
Tester maintenant la réactivité de votre interface graphique aux événements concernant le clavier
de votre calculette graphique.
2. Vous avez sûrement remarqué que la fermeture de la fenêtre correspondant à votre interface gra-
phique de calculette ne provoque pas pour autant la terminaison du processus java. Que faut-t-il
faire ?
Exercice 27.
Nous souhaitons illustrer l’utilisation d’autres composants SWING en rajoutant quelques outils de
contrôle de l’affichage de l’écran.
1. Créer un nouveau JPanel pour recevoir ces outils de configuration et le disposer au dessus de la
zone d’affichage dans le pannel correspondant à l’écran.
2. Ajouter successivement sur ce panel
(a) un JLabel étiqueté par ”Fonte”
(b) un JComboBox auquel on associera des noms de fontes
(c) deux JCheckBox auquelles on associera les chaı̂nes ”gras” et ”italique”
Ces outils seront utilisés pour contrôler le choix de la fonte utilisée pour la zone d’affichage.
16
3. Comme lors de l’exercice précédent, associer un gestionnaire d’événements aux deux JCheckBoxes
permettant de contrôler le style de l’affichage de l’écran (ici, c’est l’interface ItemListener qui doit
être implémentée).
4. De manière similaire, associer un gestionnaire d’événements à la JComboBox permettant de contrôler
la police de l’affichage de l’affichage.
1. Créer une classe GrilleMorpionG qui hérite de JComponent. Cette classe contient un attribut privé
qui sera un tableau de caractères (comme dans la classe Jeu) qui contient localement les informations
liées à la grille.
2. Ecrire le constructeur de la classe permettant entre autre l’utilisation des images O.gif et X.gif
pour représenter les coups des joueurs. A cet effet, la classe contient deux champs Image (imageX
et imageO) qui sont deux éléments dessinables dans un contexte graphique et qui font référence à
deux images de 50*50 pixels, une croix et un rond.
3. Redéfinir la méthode paintComponent qui redessine la grille à partir du tableau de caractères. Pour
tester votre code, écrivez une méthode main créant une JFrame dans laquelle vous placerez votre
composant graphique.
4. Ecrire la méthode refresh prenant en paramètre un objet de la classe Jeu qui redessine la grille en
17
Exercice 29. L’interface graphique : le joueur
Créons l’interface graphique du jeu qui est gérée par la classe JoueurHumainG qui hérite de la classe
JoueurHumain.
1. Commencer l’écriture de la classe JoueurHumainG. Cette classe possède comme attributs spécifiques :
un objet de type JFrame, sa grille graphique, deux entiers qui représentent les coordonnées que le
joueur souhaite tenter, et la référence sur un objet de la classe Jeu.
2. Ecrire le constructeur de la classe prenant en paramètre un caractère qui sera la marque du joueur.
3. Ajouter à la grille graphique un MouseListener qui sera l’implémentation anonyme d’un Mou-
seAdapter. Ce Listener récupère les coordonnées du clic gauche de la souris et les convertit en
coordonnées utilisables par le jeu. Ces coordonnées seront stockées dans les attributs entiers prévus
à cet effet.
4. Comment faire pour que les clics de la souris ne soient pris en compte que lorsque c’est au tour du
joueur ?
5. Redéfinir la méthode afficheJeu.
6. Redéfinir la méthode joue : cette méthode peut s’inspirer de celle de JoueurHumain.
7. Tester votre application en créant une partie entre deux joueurs humains, l’un en mode graphique
et l’autre en mode console. Pour cela, inspirez-vous de la classe HumainVSMachine.
La partie ne commence que lorsque l’utilisateur appuie sur le bouton ’ok’. Comme précédemment, on
souhaite que toute l’interface avec l’utilisateur soit gérée par le joueur et qu’elle reste transparente pour
le moteur. Pour cela, créez un nouveau type de joueur humain permettant à l’utilisateur d’effectuer le
choix de sa marque.
18