Les "chenilles" : Episode II
Objectifs : Il s'agit de se familiariser avec les notions d'héritage, de réutilisation et d'abstraction et de polymorphisme et à leur réalisation dans le langage JAVA.
Dans ce TP vous allez utilisez les nouvelles notions vues en cours: les classes abstraites et les interfaces.
Dans un TP précédent (TP 9) vous
avez développé une application permettant l'animation de "Chenilles" se déplaçant à l'intérieur
d'une fenêtre.
Pour cela vous avez réutilisé du code issu de l'application d'animation de visages vue lors du
premier TP
(TP1).
En particulier vous avez du modifier le code de la classe Dessin
afin de pouvoir afficher non plus des objets de classe VisageRond
,
mais des objets de classe Chenille
.
Maintenant que vous avez découvert le polymorphisme et les concepts de classes abstraites et
d'interfaces,
vous disposez des connaissances techniques qui vous permettent d'envisager une application
beaucoup plus
ambitueuse animant simultanément des objets de classe VisageRond
et
Chenille
et
pourquoi pas d'autres choses dont vous n'avez pas encore idée.
Exercice 1 : animer des visages avec les chenilles.
On souhaite modifier l'application d'animation des Chenilles, afin de pouvoir animer
simultanément
dans une même zone de dessin à la fois des objets Chenille
et
des objets VisageRond
. La figure ci-dessous montre le résultat attendu.
-
Si ce n'est déjà fait, récupérez la correction du TP Chenille
-
Décompressez l'archive que vous avez téléchargée puis ouvrez le projet correspondant sous VSCode et vérifiez que l'application
AppliNChenille
fonctionne correctement. -
Intégrez à ce projet la classe VisageRond.java vue lors des TP1 et TP2 et modifiez le code de manière à pouvoir animer simulaténement des visages et chenilles.
Quelles modifications avez-vous du apporter aux classes
VisageRond
,Chenille
etDessin
pour obtenir ce résultat ?Pour rappel, la diagramme UML des classes de l'application Chenilles
Pour la zone de dessin, ce qui importe, c'est que les objets qu'elle contient puissent
se dessiner
(plus précisément possèdent un méthode public void dessiner(Graphics g)
) et
se deplacer
(méthode public void deplacer()
. C'est le cas des
classes Chenille
et VisageRond
.
L'idée est donc de
-
Définir une interface
IAnimable
. -
Indiquer que les classes
Chenille
etVisageRond
réalisent (implémentent) cette interface. -
Modifier la classe
Dessin
, pour que la liste d'objets qu'elle contient ne soit plus une liste deChenille
mais deIAnimable
.
Exercice 2 : afficher des étoiles fixes
On souhaite modifier l'application afin de pouvoir afficher, en plus des Chenilles et Visages animées, des étoiles qui elles demeurent fixes.
Pour construire et dessiner une étoile à 5 branches vous pourrez procéder comme indiqué sur la figure suivante.
Le code java correspondant à ces différentes étapes vous est donné ci-dessous
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
int nbSommets = 5;
// Etape 1
// calcul des sommets de l'étoile
float deltaAngle = 360f / nbSommets;
float angle = -90;
Point2D.Float[] sommets = new Point2D.Float[nbSommets];
for (int i = 0; i < nbSommets; i++) {
sommets[i] = new Point2D.Float((float) Math.cos(Math.toRadians(angle)) * r,
(float) Math.sin(Math.toRadians(angle)) * r);
angle += deltaAngle;
}
// Etape 2
// construction du chemin reliant les points
Path2D contour = new Path2D.Float();
contour.moveTo(sommets[0].getX(), sommets[0].getY());
contour.lineTo(sommets[2].getX(), sommets[2].getY());
contour.lineTo(sommets[4].getX(), sommets[4].getY());
contour.lineTo(sommets[1].getX(), sommets[1].getY());
contour.lineTo(sommets[3].getX(), sommets[3].getY());
contour.closePath();
// dessin à l'aide d'un objet Graphics g
Graphics2D g2 = (Graphics2D) g.create(); // on crée une copie de g
// Etape 3
// dessin du contour
g2.setColor(couleurTrait);
g2.setStroke(new BasicStroke(8.0f));
g2.translate(x, y); // x et y le centre du cercle définissant l'étoile
g2.draw(contour);
// Etape 4
// Remplissage de la forme
g2.setPaint(couleurRemplissage);
g2.fill(contour);
Question 1:
Créez une nouvelle classe Etoile
qui permet de dessiner un étoile à 5 branches.
Pour écrire ce code, vous vous devez de répondre aux questions suivantes:
-
Quels sont les attributs qui caractérisent une étoile
-
Quel est le constructeur pour cette classe ?
-
Quelles méthodes ?
-
Comment faire pour que les objets instance de cette classe puissent être ajoutés à la zone de dessin ?
Pour pouvoir être affichée dans le dessin une étoile doit être un objet dessinable
(objet possèdant une méthode
public void dessiner(Graphics g)
)). Par contre les étoiles sont fixes,
elles n'ont pas de méthode
public void deplacer()
.
Pour pouvoir distinguer les objets pouvant se dessiner des objets pouvant se dessiner et
aussi se déplacer on peut créer
une nouvelle interface IDessinable
contenant la méthode
public void dessiner(Graphics g)
et
définir le type IAnimable
comme étant un sous type de
IDessinable
. On peut ainsi refactorer
le code de la classe dessin pour que :
-
le type du paramètre de la méthode
ajouterObjet
soit non plusIAnimable
maisIDessinable
, -
la liste des objets du dessin soit typée avec
IDessinable
au lieu deIAnimable
, -
la méthode déplaçant tous les objets ne fasse se déplacer que les objets dont le
type (la classe) implémente
l'interface
IAnimable
.
La classe Etoile
doit donc implémenter l'interface
IDessinable
.
Les attributs d'un objet étoile sont:
- les coordonnées x,y du centre de l'étoile, des entiers,
- le rayon, un entier,
- l'épaisseur du trait de contour, un flottant,
- les couleurs du trait de contour et de remplissage,
-
l'objet
Path2D
qui défini le tracé du contour de l'étoile.
L'attribut contour est calculé dans le constructeur (Etapes 1 et 2).
Il est utilisé dans la méthode dessiner
dont le code correspond aux étapes
3 et 4.
Les paramètres du constructeur permettent de fixer
les valeurs des attributs x
,y
, r
,
epaisseurTrait
, couleurTrait
et
couleurRemplissage
.
Question 2: Modifiez l'application AppliNChenilles
pour
afficher en plus des chenilles et des visages
deux étoiles, une étoile jaune à bords rouges de rayon 100 pixels, et une étoile bleue à
bords verts
de rayon 50 pixels.
Exercice 3 : afficher d'autres formes fixes: les polygones réguliers
On veut maintenant afficher un nouveau type de formes fixes: les polygones réguliers, polygones inscrits dans un cercle et aux cotés de longueur identique.
Pour un polygone régulier (par exemple un pentagone) seule l'étape 2 diffère, il suffit de prendre les sommets consécutifs
Question 1: Définir une nouvelle classe de forme fixe,
PoygoneRegulier
.
Pour écrire ce code, vous devez comme pour la classe Etoile
répondre
aux questions suivantes:
-
Quels sont les attributs qui caractérisent un polygone régulier
-
Quel est le constructeur pour cette classe ?
-
Quelles méthodes ?
-
Comment faire pour que les objets instance de cette classe puissent être ajoutés à la zone de dessin ?
Mais en plus vous devez vous posez les questions suivantes:
-
Quel est le code commun entre les classes
Etoile
etPolygoneRegulier
? -
Comment éviter de dupliquer ce code et le partager efficacement ?
PolygoneRegulier
partage une bonne partie de son code avec
Etoile
:
-
les attributs,
-
la partie du constructeur correspondant au calcul des sommets (étape1),
-
et la méthode dessiner.
Tout ce code
peut être factorisé dans une super-classe abstraite
FormeCirculaireReguliere
.
La classe PolygoneRegulier
hérite de
FormeCirculaireReguliere
implemente simplement
un constructeur qui réalise l'initialisation du contour (étape 2).
La classe Etoile
elle aussi, de manière similaire à
PolygoneRegulier
hérite
de FormeCirculaireReguliere
et réalise l'initialisation du contour (étape
2) dans son constructeur.
Question 2: Modifiez l'application
AppliNChenilles
pour en plus des objets précédents afficher
un pentagone et un octogone que vous placerez où bon vous sied.
Exercice 4 : Généralisation des formes
La généralisation des concepts Etoile et PolygoneRegulier en FormeCirculaire a permis de factoriser le code commun à ces deux types de formes. Cette factoristaion peut être poussée plus loin pour offrir plus de généralité et faciliter des évolutions futures du logiciel en permettant d'intégrer facilement n'importe quel type de formes.
Question : définir deux nouvelles classes abstraites Forme et FormeCirculaire qui généralisent le concept de FormeCirculaireRégulière comme le montre la figure ci-dessous.
Exercice 5 : animer les formes
On souhaite maintenant étendre l'application de façon à ce que les formes (étoiles, polygones réguliers,...) puissent être au choix :
- fixes comme précédemment,
- animées.
Dans le cas où la forme est animée, plusieurs types de mouvements sont possibles (voir figure 3):
-
un déplacement analogue à celui des
VisagesRond
, c'est à dire que la forme se déplace dans une direction et rebondit sur les bords de la fenêtre quand elle les touche, - un déplacement circulaire, c'est à dire que la forme tourne autour d'un point, centre de rotation.
Question 1: Comment procéderiez vous pour permettre la cohabitation de formes fixes et de formes animées, sachant que les formes animées doivent pouvoir être dotées de différents types de trajectoire ? Quelle modélisation proposeriez vous pour permettre cela tout en garantissant une extensibilité aisée de l'application (c'est à dire de pouvoir ultérieurement ajouter facilement de nouveaux types de formes et de trajectoires) ?
Pour synthétiser vos propositions, donnez un diagramme de classes UML représentant les différentes classes et interfaces de votre application et leurs relations.
Essayez de trouvez une solution par vous-même. Cependant, si vous n'avez pas d'idées sur la manière de procéder, le bouton ci dessous vous fournira quelques indications.L'idée est de définir une classe FormeAnimee qui associe une Forme à un IAnimateur (type abstrait pour un animateur) et ensuite de créer différentes classes d'animateurs réalisant l'interface IAnimateur :
- AnimateurCirculaire qui anime une forme se déplaçant sur un cercle,
- AnimateurRebonde qui anime une forme se déplaçant selon une trajectoire rectiligne et rebondissant lorsqu'elle touche les bords du dessin,
Question 2: Modifiez votre application afin d'intéger les modifications précédentes.
Les parties précédentes avaient pour objectif de vous familiariser avec les notions de classes abstraites et d'interface en Java. Les parties qui suivent sont "facultatives", dans le sens où elles n'introduisent pas de nouvelles notions. Aussi ne paniquez pas si vous n'avez pas le temps de les faire, vous pourrez vous contenter de regarder les vidéos de solutions qui seront proposées (bientôt :-)).
Exercice 6 : un nouveau type d'animateur : AnimateurCap
On voudrait maintenant que les formes circulaires (étoiles, polygones réguliers) puissent être également dotées d'un mouvement analogue à celui des Chenilles : déplacement avec un variation aléatoire d'un cap sans jamais sortir de la zone de dessin.
Question 1: Comment procéderiez vous pour mettre en oeuvre ce nouveau type d'animateur ?
Pour synthétiser vos propositions, donnez un diagramme de classes UML représentant les différentes classes et interfaces pour les animateur et leurs relations.
Essayez de trouvez une solution par vous-même. Cependant, si vous n'avez pas d'idées sur la manière de procéder, le bouton ci dessous vous fournira quelques indications.On s'aperçoit qu'un AnimateurCap va devoir de la même manière qu'un AnimateurRebond tester que la forme qu'il anime ne sort pas de la zone de dessin. Afin de partager ce code commun, l'idée est de définir une classe abstraite AnimateurAvecDess dans laquelle on fait remonter les methodes sortAGauche(), sortADroite(), sortEnHaut() et sortEnBas() et des définir les classes AnimateurRebond et AnimateurCap comme des sous classes de cette classe abstraite.
Question 2: Modifiez votre application afin d'intéger ce nouveau type d'animateur.
Exercice 7 : permettre différents comportements d'animation pour les Chenilles
On voudrait maintenant que les chenilles, de manière similaire aux formes, puissent être animées avec au choix : un déplacement aléatoire, un déplacement avec rebondissement sur les bords de la fenêtre ou un déplacement circulaire. La vidéo ci dessous montre le résultat attendu.
Question 1: Quelles ajout/modifications doivent être apportées à votre code pour offrir ces nouvelles fonctionnalités ? Modifiez votre diagramme de classes UML pour de manière synthétique la nouvelle organisation des classes et interface de votre application.
Essayez de trouvez une solution par vous-même. Cependant, si vous n'avez pas d'idées sur la manière de procéder, le bouton ci dessous vous fournira quelques indications.Au niveau de Chenille il faut déléguer l'animation de la tête à un IAnimateur et pour cela faire rentrer les Anneaux dans une hiérarchie de type uniforme avec Forme en introduisant un nouveau type abstrait (interface) IForme.
Question 2: Modifiez votre code afin de prendre en compte cette nouvelle modélisation.