Anda di halaman 1dari 5

Universite Paris 7

Licence Bio Info

Programmation objet
Annee 2004-2005

TD n7 - Correction
Un mod`ele de conception pour les applications avec interfaces graphiques
Le Mod`ele-Vue-Controleur
Le TD daujourdhui est tr`es fortement en rapport avec le projet. Un peu dattention sil
vous plat !
Exercice 1 Ce mod`ele de conception est lun des plus dur `a comprendre. Non pas tant par
sa complexite, mais parce quil y a `a peu pr`es autant dinterpretations de ce mod`ele, depuis
sa premi`ere version Smalltalk, quil y a douvrages en parlant. Cette mise en garde concernant
les renseignements que vous pourriez avoir envie de chercher par vous meme, on va ici vous
presenter une seule version et tout devrait bien se passer.
Ce mod`ele consiste `a decoupler une application en trois principaux roles (le mod`ele, la
vue, et le controleur, donc), afin que chaque role puisse etre modifie ou remplace facilement,
et independamment des autres. Les differents adeptes du MVC sont neanmoins grosso-modo
daccord sur ces points :
Le mod`ele contient les donnees de lapplication.
La vue est la representation des donnees pour lutilisateur. Il peut y en avoir plusieurs,
eventuellement.
Le controleur permet `a lutilisateur de modifier les donnees de lapplication. Il peut aussi
eventuellement y en avoir plusieurs.
Le mod`ele, meme sil fait des calculs, doit etre independant de la facon dont on presente
les donnees.
Et bien sur, comme tout le temps en objet, plus on peut encapsuler, mieux cest.
Les differents MVC diff`erent surtout par les responsabilites accordees `a la vue et au contr
oleur
(quid de linterface graphique, quand on clique sur une zone de lecran par exemple ? O`
u est
la fronti`ere entre mod`ele et controleur ?) et par la nature du couplage entre les roles (on a au
moins un controleur capable dagir sur un mod`ele capable dagir sur une vue, mais en pratique,
ca suffit rarement, comme en Java ou beaucoup de composants sont `a la fois vue et controleur).
On veut realiser un jeu pour entraner des non-informaticiens `a la technique subtile du clicgauche.
La fenetre principale du jeu presente une zone carree, initialement verte, et un affichage du
score total. Lorsquon clique dans le carre, on gagne un point, sinon, on en perd un. De plus
lorsquon clique sur la zone carree, elle saffiche en rouge. Si on clique `a cote, elle saffiche en
vert.
Pour la modelisation, dans cet exercice, on ajoute comme contrainte quon veut pouvoir
ulterieurement ajouter dautres representations du jeu, par exemple, entraner lutilisateur `a
viser sur le clavier en tapant la lettre C pour viser le carre, ou aussi modifier le programme
pour lui faire viser une cible ronde. Cela implique de bien rendre le mod`ele independant `a la fois
du controleur et de la vue. On ne vous demande pas de realiser ces modifications, juste de faire
en sorte quelles soient realisables sans modifier le mod`ele.
1

Les classes de la biblioth`eque standard dont vous aurez besoin :


java.awt.Color , javax.swing.JFrame , java.awt.Dimension , java.awt.Graphics,
javax.swing.JPanel que vous avez dej`
a vues la semaine derni`ere.
java.awt.event.MouseAdapter, java.awt.event.MouseEvent qui vous permettront
de gerer les clics souris. Comme la semaine derni`ere, nhesitez pas `a commencer `a faire des
impressions sur la console pour comprendre quand les methodes sont appelees.
Commencez par modeliser soigneusement votre programme en definissant le mod`ele, la (ou
les ) vues, et le controleur.
Vous ferez tout dabord cet exercice avec une fenetre de taille fixe, 400 par 400, et un carre
au milieu de la fenetre de taille 100 par 100. Vous ecrirez le score o`
u vous voulez dans le meme
composant graphique.
1. Decrivez les responsabilites de chacun des roles du MVC, et degagez-en les relations dont
vous allez avoir besoin entre ces classes. Tracez un diagramme dynamique au besoin.
2. Decrivez precisement le mod`ele et le contr
oleur. Decrivez la methode paint de votre vue.
3. Implementez votre solution, en utilisant des commentaires au format javadoc.
Correction :
// fichier Cible.java
import java.awt.Color;
import javax.swing.JFrame;
public class Cible extends JFrame{
public static final int TAILLE = 400 ;
public static final int CENTRE = TAILLE / 2 ;
public static final Color TOUCHE = Color.RED ;
public static final Color RATE = Color.GREEN ;
public static final int RAYON = 100 ;
public Cible(){
super("Cible") ;
VueCible vue = new VueCible();
ModeleCible modele = new ModeleCible(vue);
ControleurCible controleur = new ControleurCible(modele) ;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) ;
setContentPane(vue) ;
vue.setControleur(controleur) ;
}
public static void main(String[] args) {
Cible c = new Cible() ;
c.pack() ;
c.setResizable(true) ;
c.setVisible(true) ;
}
}
// fichier ControleurCible.java
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class ControleurCible extends MouseAdapter{


private ModeleCible modele ;
public ControleurCible(ModeleCible modele){
this.modele = modele ;
}
private boolean coordDansCarre(int x , int y){
return ( (x > (Cible.CENTRE - (Cible.RAYON/2))) &&
(x < (Cible.CENTRE + (Cible.RAYON/2))) &&
(y > (Cible.CENTRE - (Cible.RAYON/2))) &&
(y < (Cible.CENTRE + (Cible.RAYON/2))) ) ;
}
public void mouseClicked(MouseEvent e){
int x = e.getX() ;
int y = e.getY() ;
System.out.println("click sur " + x + " " + y) ;
if (coordDansCarre(x,y))
modele.updateZone(true) ;
else
modele.updateZone(false) ;
}
}
// fichier ModeleCible.java
public class ModeleCible {
private VueCible vue ;
private boolean currentZone ;
private int currentPoints ;
public ModeleCible(VueCible vue){
this.vue = vue ;
currentPoints = 0 ;
}
public void updateZone( boolean zone ){
currentZone = zone ;
if (zone)
currentPoints ++ ;
else
currentPoints -- ;
vue.update(currentZone , currentPoints) ;
}
}
// fichier VueCible.java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
/**
* La vue est ici `
a la fois:
* - la repr
esentation sous forme de dessin de l
etat du jeu: traduire les donn
ees du mod`
ele
* en leur repr
esentation graphique (t^
ache duale de celle du contr^
oleur)
* - linterface utilisateur qui envoie les
ev
enements bruts au contr^
oleur
* pour constitution des requ^
etes `
a envoyer au mod`
ele.

*/
public class VueCible extends JPanel{
private boolean touche;
private int score;
public VueCible(){
touche = false ;
score = 0 ;
}
private void dessineCarre(Graphics g){
g.setColor(Color.BLACK) ;
g.drawRect( Cible.CENTRE - (Cible.RAYON/2),
Cible.CENTRE - (Cible.RAYON/2) ,
Cible.RAYON ,
Cible.RAYON) ;
}
private void colorieCarre(Color color , Graphics g){
g.setColor(color) ;
g.fillRect( Cible.CENTRE - (Cible.RAYON/2),
Cible.CENTRE - (Cible.RAYON/2) ,
Cible.RAYON ,
Cible.RAYON) ;
}
private void colorieFond(Graphics g){
g.setColor(Color.WHITE) ;
g.fillRect(0,0,getWidth() , getHeight()) ;
}
private void afficheScore(Graphics g){
g.setColor(Color.BLACK) ;
g.drawString("Score: " + score ,
(Cible.TAILLE - Cible.RAYON)/2,
(Cible.TAILLE - Cible.RAYON)/4 ) ;
}
public void paint(Graphics g){
colorieFond(g) ;
if (touche)
colorieCarre(Cible.TOUCHE, g) ;
else
colorieCarre(Cible.RATE, g) ;
dessineCarre(g) ;
afficheScore(g);
}
public void update(boolean touche, int score) {
this.touche = touche ;
this.score = score ;
repaint() ;
}
public void setControleur(ControleurCible controleur) {
addMouseListener(controleur) ;
}
public Dimension getPreferredSize(){

return new Dimension(Cible.TAILLE , Cible.TAILLE) ;


}
}

Dans un deuxi`eme temps, vous pourrez ameliorer votre programme, voici deux idees dameliorations,
mais vous pouvez laisser libre court `a votre imagination :
1. Faites en sorte que votre fenetre soit de taille modifiable par lutilisateur, tout en conservant
la cible au milieu de la fenetre et de la meme taille.
2. Ajoutez un composant texte (un JLabel par exemple) et definissez le comme une nouvelle vue du mod`ele. Essayez de generaliser votre programme afin quon puisse ajouter de
nouvelles vues au mod`ele sans avoir `a modifier cette classe.
Correction :
1. Au lieu dutiliser les constantes de la classe CibleTAILLE et CENTRE, vous pouvez les calculer
dynamiquement `a laide des methodes getWidth() et getHeight()qui renvoient la taille actuelle
du composant.
2. Inspirez vous du TD precedent et utilisez une interface Vue qui sert `a definir un observateur du
mod`ele. Le mod`ele aura alors une liste des vues qui lobservent, et pourra les actualiser en appellant
sur tous ses vues la methode update(currentZone , currentPoints)