Etude de cas : le compteur de clicks

D'aprs le livre de Fintan Culwin : a Java GUI Programer's Primer, Prentice Hall

Enoncé

Un petit dispositif, représenté par une interface graphique, a pour fonction de compter les clicks réalisés avec le bouton de la souris.
L'interface graphique est constituée : Le comptage est effectué dans un intervalle MIN..MAX. Au dessus de cette valeur, le compteur conserve la  valeur MAX si un click survient.

Analyse

En général, la première chose à faire est de diviser l'application en plusieurs composants dont on définit l'interface puis l'implantation.
Pour notre problème, l'application nécessite une interface utilisateur. Il est donc naturel de séparer ces 2 aspects en composants distincts.

Composant applicatif : modèle structurel

les attributs identifiés sont :

Composant applicatif : Type Abstrait de Données
type        CompteurDeClicks
utilise     Entier,booléen
opérations    
    construire :    -> CompteurDeClicks
    construire :    Entier*Entier -/> CompteurDeClicks
    remiseAZero :   CompteurDeClicks -> CompteurDeClicks
    incrementer :   CompteurDeClicks -> CompteurDeClicks
    compteEst   :   CompteurDeClicks -> Entier
    auMaximum   :   CompteurDeClicks -> Booléen
préconditions
    construire(a,b) : si a,b appartiennent à [valeurMaximum..valeurMinimum]
sémantique
    compteEst(incrementer(C))= si compteEst(C)=valeurMaximum
                                alors valeurMaximum
                                sinon compteEst(C)+1
Composant applicatif : modèle dynamique

figure 3.4
Implantation Java
 
public class CompteurDeClicks{
    private static final int MAXIMUM=10;    // Les valeurs par défaut sont implantées 
    private static final int MINIMUM=0;    // par des constantes
    private int valeurMaximum;        // Les attributs sont privés
    private int valeurMinimum;        // Accès contrôlé par des méthodes
    private int valeurCourante;
    // Méthodes publiques
    // Les constructeurs
    public CompteurDeClicks(){
        valeurMaximum=MAXIMUM;
        valeurMinimum=MINIMUM;
    }
    public CompteurDeClicks(int min, int max){
        valeurMaximum=max;
        valeurMinimum=min;
    }
    public void remiseAZero(){
        valeurCourante=valeurMinimum;
    }
    public void incrementer(){
        if (valeurCourante!=valeurMaximum)
            valeurCourante++;
    }
    public int compteEst(){
        return valeurCourante;
    }
    public boolean auMaximum(){
        return valeurCourante==valeurMaximum;
    }
}
Analyse de l'interface

De même que les fonctionnalités de l'application doivent être séparées de celles de l'interface, de même l'interface doit être  divisé en 2 parties :

L'utilisateur interagit avec le composant de présentation par le biais de boutons, zone de dialogues, etc.... Cette interaction génère des évènements. Ces évènements sont envoyés au composant de liaison. On peut dire que le composant de liaison écoute les évènements. Le composant de liaison communique avec le composant applicatif qui exécute le traitement associé à l'évènement enregistré. Le composant de liaison appelle ensuite des méthodes de la présentation qui a la responsabilité de modifier l'apparence de l'interface.

Modèle de gestion des évènements en Java

figure 3.5
Architecture de l'application

figure 3.6
La classe CompteurDeClicksPresentation

Elle représente l'interface graphique de l'objet applicatif.
Elle est formé à partir de composants graphiques prédéfinis dans les bibliothèques Java (Panel, Label, Button) selon la hiérarchie suivante.

figure 3.7
Les 3 composants visuels sont représentés par les feuilles de la hiérarchie.

Cette classe a pour responsabilité :

Ses méthodes sont : Image graphique l'interface

figure 3.8
Implantation java de la classe CompteurDeClicksPresentation
 
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
public class CompteurDeClicksPresentation{
    private Button boutonIncrement;    
    private Button boutonRaZ;
    private Label valeurAffichage;
    // Méthodes publiques
    // Constructeur
    public CompteurDeClicksPresentation(CompteurDeClicksLiaison sonApplet){
        Panel zoneAffichage = new Panel();
        Panel zoneControle = new Panel();;
        sonApplet.setLayout(new GridLayout(2,1,10,10));
        valeurAffichage=new Label();
        zoneAffichage.add(valeurAffichage);
        sonApplet.add(zoneAffichage);
        boutonIncrement=new Button("+");
        boutonIncrement.setActionCommand("incrementer");
        boutonIncrement.addActionListener(sonApplet);
        boutonRaZ=new Button("RaZ");
        boutonRaZ.setActionCommand("RemiseAZero");
        boutonRaZ.addActionListener(sonApplet);
        zoneControle.add(boutonIncrement);
        zoneControle.add(boutonRaZ);
        sonApplet.add(zoneControle);
    }
// méthodes permettant le changement d'apparence de l'interface en réponse aux actions de l'utilisateur
    public void majValeur(int val){
        valeurAffichage.setText(Integer.toString(val));
    }
// Méthode de gestion des changements d'états
    public void majEtatMaximum(){
        boutonIncrement.setEnabled(true);
        boutonRaZ.setEnabled(true);
    }
    public void majEtatMinimum(){
        boutonIncrement.setEnabled(true);
        boutonRaZ.setEnabled(false);
    }
    public void majEtatInter(){
        boutonIncrement.setEnabled(true);
        boutonRaZ.setEnabled(true);
    }

}
La classe CompteurDeClicksLiaison
import java.awt.*;
import java.applet.Applet;
import java.awt.event.*;
import CompteurDeClicks;
import CompteurDeClicksPresentation;
public class CompteurDeClicksLiaison extends Applet implements ActionListener{
    private final int ETAT_INITIAL=0;
    private final int ETAT_MINIMUM=1;
    private final int ETAT_COURANT=2;
    private final int ETAT_MAXIMUM=3;
    private int etatCourant=ETAT_INITIAL;
    private CompteurDeClicks leCompteur;
    private CompteurDeClicksPresentation sonInterface;
// La méthode init est automatiquement invoquée après l'exécution du constructeur de l'applet
// elle crée les instances utilisées par l'interface, elle les place sur le dispositif d'affichage 
// et les met dans un état initial
    public void init(){
        leCompteur = new CompteurDeClicks(0,100);
        sonInterface = new CompteurDeClicksPresentation(this);
        sonInterface.majValeur(leCompteur.compteEst());
        sonInterface.majEtatMinimum();
        etatCourant=ETAT_MINIMUM;
    }
// Les composants qui ont enregistré ce "listener" lui envoient des évènements.
// Elle implante la réaction de l'application aux évènements qui surviennent
    public void actionPerformed(ActionEvent evt){
        String boutonPresse=event.getActionCommand();

       if (boutonPresse.equals("incrementer")){
        if (etatCourant==ETAT_MINIMUM){
           // transition de l'état MINIMIM à l'état COURANT
                sonInterface.majEtatInter();
                etatCourant=ETAT_COURANT;
            }
          leCompteur.incrementer();
        if (etatCourant==ETAT_MAXIMUM){
                sonInterface.majEtatMaximum();
                etatCourant=ETAT_MAXIMUM;
            }
       }else if (boutonPresse.equals("RemiseAZero")){
            leCompteur.remiseAZero();
            sonInterface.majEtatMinimum();
            etatCourant=ETAT_MINIMUM;
        }
       sonInterface.majValeur(leCompteur.compteEst());
    }
}
 

Et maintenant a vous de jouer