Exercices

Classe abstraite

Supposons que l’on puisse garer autre chose que des voitures (des motos, des camions). Sur quel type pourrait-on appuyer la réalisation de notre code de Garage?

détails :

Des classes concrètes:

Interface de service

Supposons que le garage qui nous occupe veuille disposer d’un Catalogue de Voiture (ou de Vehicule si on a réalisé l’exercice précédent).

Une première version de ce Catalogue est en fait un système de recherche simple: à partir d’une référence on rend la Voiture (ou le Vehicule) correspondant. L’application de gestion du garage peut évoluer (au départ on dispose d’un catalogue simple s’appuyant sur un code java de test ou sur une liste contenu dans un fichier; ensuite on déploie avec une base de données ou en transmettant la requête à une machine "serveur").

  • Définir le type CatalogueVoiture (ou CatalogueVehicule)

  • Mettre en place un SimulacreCatalogue : un petit code de test qui part d’un tableau de Voiture (ou de Vehicule) et qui répond aux demandes qui lui sont faites (on donne une référence et il rend l’objet correspondant).

  • adapter ce code:

package com.garaje.ihm;

import com.garaje.commons.CatalogueVehiculeSimple;
import com.garaje.commons.Vehicule;
import java.io.Console;

/**
 * peut-être modifié de manière à ne pas utiliser Console
 * mais des interactions directes depuis JShell
 * @author pbamade
 */
public class SimpleIHMCatalogue { // implements Runnable!
   private CatalogueVehiculeSimple catalogue ;
   private Console console ;

   public SimpleIHMCatalogue(CatalogueVehiculeSimple catalogue) {
      this.catalogue = catalogue;
      this.console = System.console() ;
      if(this.console == null ){
         throw new RuntimeException("no Console!") ;
      }

   }

   public void run() {
      String question = null ;
      while(null != (question = this.console.readLine("réference cherchée : "))) {
         Vehicule trouvé = catalogue.get(question);
         if (trouvé != null) {
            console.printf("vhicule trouvé : %s", trouvé.toString());
         } else {
            console.printf("référence non trouvée") ;
         }
      }

   }
}

pour tester votre réalisation

Propositions de solutions:

(Bien noter que le code d’interaction ne sait pas avec quel catalogue il travaille -n’importe lequel fera l’affaire!).

Le "pattern" Observer/Observable

(Exercice indépendant des précédents)

ATTENTION: les codes Observer/observable sont obsoletes (deprecated) à partir de Java 9! Nous les avons conservés ici pour des raisons pédagogiques mais il serait intéressant de reprendre cet exercice avec des classes de java.util.concurrent.Flow que nous verrons ultérieurement.

Une classe qui hérite de java.util.Observable est conçue pour prévenir d’autres codes que son état a changé (forme simplifiée de "Modèle-Vue") Une classe Observable ne sait pas a priori ce que les codes qui implantent le contrat Observer font. Ces codes ne sont pas couplés avec le code courant.

  • Changer le code Garage et faites le hériter de Observable. Dotez le d’une méthode indiquant le nombre de places libres.

  • Quand une voiture est "garée" passez cette voiture en argument à la méthode de notification.

  • Ecrire un code qui implante l’interface Observer et qui affiche (simplement par System.out.println) une trace de la voiture qui vient de rentrer.

  • Ecrire un autre code de ce type qui affiche le nombre de places libres.

  • Testez ces codes.

Conception avec des types abstraits

Ces exercices sont plus complexes mais méritent toute votre attention (dans le cadre de révisions par exemple).

Reprendre la classe Vehicule décrite ci-dessus:

Pour gérer les événements survenus dans la gestion du garage on pourrait adopter une stratégie légèrement différente. Au lieu d’avoir une notification non typée (update prend un Object en paramètre) on pourrait considérer que l’on a:

  • d’une part des objets qui décrivent l'événement survenu dans le Garage. (et c’est ce type qui sera utilisé explicitement en argument de méthode de notification)

  • d’autre part au lieu d’avoir une seule méthode de notification on en aura deux: une méthode qui porte notification d’une entrée et une autre qui porte notification d’une sortie.

On aura donc à redéfinir à notre manière l'Observer avec deux méthodes (chacune avec un argument typé)

Suggestions: l’interface correspondante s’appuie sur cet événement

L'événement lui même est découplé du Garage qui est donc représenté par cette interface

Le codage du garage concret (qui est un autre type d'Observable) est reporté à la leçon suivante.

Utilisation avancée des interfaces

Ici pas de code à écrire! juste du code à lire et à comprendre.

On reprend l’idée du Catalogue de Vehicules :

  • Avec ce code:

    package com.garaje.commons;
    
    public interface CatalogueVehicule extends CatalogueVehiculeSimple{
       /**
        *
        * @param critère
        * @return un tableau vide si aucun véhicule trouvé
        */
       Vehicule[] get(CritereChoixVehicule critère) ;
    
    }
  • basé sur cette interface:

    package com.garaje.commons;
    
    /**
     * ceci n'est pas tout à fait un objet-fonction (voir utilisation dans CatalogueVehicule).
     * mais en joue le rôle
     * Cette notion permet d'introduire les "fermetures" (Closures)
     *
     * @author pbamade
     */
    public interface CritereChoixVehicule {
       boolean choisir(Vehicule vehicule) ;
       /**
        * @return forme affichable du critère de choix
        */
       String toString() ;
    
    }
  • Une implantation:

    package com.garaje.commons;
    
    /**
     *
     * @author pbamade
     */
    public class MarqueRessembleA implements CritereChoixVehicule {
    
       private  String masqueMarque;
    
       public MarqueRessembleA(String masqueMarque) {
          this.masqueMarque = masqueMarque;
       }
    
    
       @Override
       public boolean choisir(Vehicule vehicule) {
          return vehicule.getMarque().contains(masqueMarque) ;
       }
    
       public String getMasqueMarque() {
          return masqueMarque;
       }
    
       public void setMasqueMarque(String masqueMarque) {
          this.masqueMarque = masqueMarque;
       }
    
       public String toString() {
          return "Marque contient : " + this.masqueMarque ;
       }
    
    }
  • Une autre implantation:

    package com.garaje.commons;
    
    import java.math.BigDecimal;
    
    public class PrixInferieurA implements CritereChoixVehicule {
    
       private BigDecimal valeurMax ;
    
       public PrixInferieurA(String valeur) {
          this.valeurMax = new BigDecimal(valeur) ;
       }
       @Override
       public boolean choisir(Vehicule vehicule) {
          return this.valeurMax.compareTo(vehicule.getPrixTTC()) > 0 ;
       }
    
       public BigDecimal getValeurMax() {
          return valeurMax;
       }
    
       public String toString() {
          return "Prix inférieur à : " + this.valeurMax.toPlainString();
       }
    
       public void setValeurMax(BigDecimal valeurMax) {
          this.valeurMax = valeurMax;
       }
    
    }
  • Un simulacre de Catalogue:

    package com.garaje.simulacres;
    
    import com.garaje.commons.CatalogueVehicule;
    import com.garaje.commons.CritereChoixVehicule;
    import com.garaje.commons.Vehicule;
    import com.garaje.deploy.Moto;
    import com.garaje.deploy.Voiture;
    import java.util.Arrays;
    
    public class PseudoCatalogue implements CatalogueVehicule {
           static Vehicule[] tableauBidon = {
          new Voiture("EZ44", "Wartburg", "1020", 425,220),
          new Moto("HHX", "Mamuth", "986", 160, 120),
          //etc, etc...
    
    
       } ;
    
       @Override
       public Vehicule get(String référence) {
          for(Vehicule vehicule : tableauBidon) {
             if(vehicule.getRef().equals(référence)) {
                return vehicule ;
             }
          }
          return null ;
       }
       @Override
       public Vehicule[] get(CritereChoixVehicule critère) {
          // ne pas copier: ceci fonctionne mieux avec un ArrayList
          Vehicule[] res = new Vehicule[tableauBidon.length];
          int nbChoix = 0 ;
          for(int ix = 0 ; ix < tableauBidon.length ; ix++){
             Vehicule vehicule = tableauBidon[ix] ;
             if(critère.choisir(vehicule)) {
                res[nbChoix++] = vehicule ;
             }
          }
          return Arrays.copyOf(res, nbChoix);
       }
    }

Un "main" de test:

package com.garaje.tests;

import com.garaje.commons.CatalogueVehicule;
import com.garaje.commons.PrixInferieurA;
import com.garaje.commons.Vehicule;
import com.garaje.simulacres.PseudoCatalogue;
import java.util.Arrays;

public class MainCritere {
   public static void main(String[] args) {
      CatalogueVehicule catalogue = new PseudoCatalogue() ;
      Vehicule[] sélection = catalogue.get(new PrixInferieurA("12000")) ;
      System.out.println(" sélection :" + Arrays.toString(sélection));

   }

}

Cette technique consiste à paramétrer un comportement en encapsulant ce comportement dans un objet (objets "commandes").

Exemples

Classe abstraite

package com.garaje.commons;

import com.garaje.utils.NegativeValueException;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 *
 * @author pbamade
 */
public abstract class Vehicule {

   private String ref;
   private String marque;
   private Empatement empatement;
   private BigDecimal prixHT;

   protected Vehicule(String ref, String marque, Empatement empatement, BigDecimal prixHT) {
      this.ref = ref;
      this.marque = marque;
      this.empatement = empatement;
      this.setPrixHT(prixHT);
   }

   protected Vehicule(String ref, String marque, Empatement empatement, String stringPrix) {
      this(ref, marque, empatement, new BigDecimal(stringPrix));
   }

   public String getRef() {
      return ref;
   }

   public String getMarque() {
      return marque;
   }

   public Empatement getEmpatement() {
      return empatement;
   }

   public BigDecimal getPrixHT() {
      return prixHT;
   }

   public final void setPrixHT(BigDecimal prixHT) {
      if (prixHT.signum() < 0) {
         throw new NegativeValueException(prixHT);
      }
      this.prixHT = prixHT.setScale(2, RoundingMode.HALF_EVEN);
   }

   public final void setPrixHT(String stringPrix) {
      this.setPrixHT(new BigDecimal(stringPrix));
   }

   public BigDecimal getPrixTTC() {
      //@TODO: vérifier si getTauxTVA rend bien un nombre positif!
      BigDecimal res = this.prixHT.multiply(this.getTauxTVA());
      return res.setScale(0, RoundingMode.HALF_EVEN);
   }

   /**
    *
    * @return un nombre strictement positif qui doit être le facteur multiplicatif
    * représentant le taux de TVA (donc quelques chose comme "1.196")
    */
   protected abstract BigDecimal getTauxTVA();

   /* plusieurs version possibles pour equals :
    * <P>
    * Ici la règle s'impose à tous les Véhicules seule la réféence unique
    * compte. Attention à ne pas modifier cette règle.
    * il faut respecter les notions de reflexivité et de transitivité.
    */
   public boolean equals(Object autre) {
      if (!(autre instanceof Vehicule)) {
         return false;
      }
      Vehicule autreVehicule = (Vehicule) autre;
      return this.getRef().equals(autreVehicule.getRef());
   }

   public String toString() {
      return this.getClass().getSimpleName() + ":"
         + this.getRef() + " "
         + this.getMarque();
   }
}
package com.garaje.commons;

import com.garaje.utils.NegativeValueException;

/**
 * Empatement d'un véhicule en centimètres.
 * @author pbamade
 */
public class Empatement {
    /**
     * longueur en cm
     */
    public final int longueur ;
    /**
     * largeur en cm
     */
    public final int largeur ;

    public Empatement(int longueur, int largeur) {
        if( (longueur < 0) || (largeur< 0 )) {
            throw new NegativeValueException(longueur, largeur);
        }
        this.longueur = longueur;
        this.largeur = largeur;
    }
}
package com.garaje.utils;

import java.util.Arrays;

/**
 * this class is for illegal arguments that yield a negative values.
 * It keeps a record of all numeric arguments in a call.
 * @author pbamade
 */

public class NegativeValueException extends IllegalArgumentException{
   private static final long serialVersionUID = 2163863881529917867L;
   private final Number[] args ;
    public Number[] getValues() {
        return args.clone() ;
    }

    public NegativeValueException(Number... args) {
        this.args = args;
    }

    public String toString() {
        return super.toString() + " "
                + Arrays.toString(args) ;
    }
}
package com.garaje.deploy;

import com.garaje.commons.Empatement;
import com.garaje.commons.Vehicule;
import java.math.BigDecimal;

/**
 *
 * @author pbamade
 */
public class Voiture extends Vehicule {

   public Voiture(String ref, String modele, String strPrixHT, int longueur, int largeur){
      super(ref, modele,new Empatement(longueur, largeur), strPrixHT) ;

   }

   // bien sûr c'est stupide: voir discussion sur ressources
   private static final BigDecimal TVA = new BigDecimal("1.196");
   @Override
   protected BigDecimal getTauxTVA() {
      return TVA ;
   }

}
package com.garaje.deploy;

import com.garaje.commons.Empatement;
import com.garaje.commons.Vehicule;
import java.math.BigDecimal;

/**
 *
 * @author pbamade
 */
// pour les besoins: une copie simpliste de Voiture
public class Moto extends Vehicule  {

   public Moto(String ref, String modele, String strPrixHT, int longueur, int largeur) {
      super(ref, modele,new Empatement(longueur, largeur), strPrixHT) ;

   }

   // toujours stupide!
   private static final BigDecimal TVA = new BigDecimal("1.21");

   @Override
   protected BigDecimal getTauxTVA() {
      return TVA ;
   }

}

Interface de service

package com.garaje.commons;

/**
 *
 * @author pbamade
 */
public interface CatalogueVehiculeSimple {

   /**
    *
    * @param référence référence du véhicule cherché
    * @return null si véhicule pas trouvé
    */
   Vehicule get(String référence) ;

}
package com.garaje.simulacres;

import com.garaje.commons.CatalogueVehiculeSimple;
import com.garaje.commons.Vehicule;
import com.garaje.deploy.Moto;
import com.garaje.deploy.Voiture;

/**
 *
 * @author pbamade
 */
public class PseudoCatalogueSimple implements CatalogueVehiculeSimple {
   static Vehicule[] tableauBidon = {
      new Voiture("EZ44", "Wartburg", "1020", 425,220),
      new Moto("HHX", "Mamuth", "986", 160, 120),
      //etc, etc...


   } ;

   @Override
   public Vehicule get(String référence) {
      for(Vehicule vehicule : tableauBidon) {
         if(vehicule.getRef().equals(référence)) {
            return vehicule ;
         }
      }
      return null ;
   }

}
package com.garaje.tests;

import com.garaje.commons.CatalogueVehiculeSimple;
import com.garaje.ihm.SimpleIHMCatalogue;
import com.garaje.simulacres.PseudoCatalogueSimple;

/**
 * voir avec Jshell est une modification de SimpleIHMCatalogue
 * @author pbamade
 */
public class MainSimple {
   public static void main(String[] args) {
      CatalogueVehiculeSimple catalogue =
         new PseudoCatalogueSimple() ;
      SimpleIHMCatalogue ihm = new SimpleIHMCatalogue(catalogue) ;
      ihm.run() ;

   }

}

Le "pattern" Observer/Observable

package com.grandgarage.biz;
import java.util.Observable;
import static java.awt.Color.* ;
// niveau du système de rapport
import static java.lang.System.Logger.Level.*;

public class GarageC1 extends Observable {
    private VoitureV4[] parking  ;
    private int nbVoitures ;
    private int tailleParking ;

    public GarageC1(int taille) {
        // on pourrait controler la taille !
        this.tailleParking = taille ;
        parking = new VoitureV4[taille] ;
    }

    public int garer(VoitureV4 voiture)throws ExceptionContenance {
        // le parking est-il plein?
        if( this.nbVoitures >= this.tailleParking) {
            throw new ExceptionContenance(this, voiture) ;
        }
       for (int index = 0; index < parking.length; index++) {
            if (null == parking[index]) {
                parking[index] = voiture;
                this.nbVoitures++;
                this.setChanged();
                this.notifyObservers(voiture);
                return index;
            }
        }
        return -1; // anomalie!!!!
   // devrait etre
   // throw new AssertionError("parking non plein et place non trouvée!");
    }

    // pas de retirer !

    public int placesLibres() {
       return this.tailleParking - this.nbVoitures ;
    }

    public double getValeur() {
        double res = 0;
        for (int ix = 0; ix < parking.length; ix++) {
            if (null != parking[ix]) {
                res += parking[ix].getPrixTTC();
            }
        }
        return res;
    }

    public static void main(String[] args) {
        try {
            GarageC1 garage = new GarageC1(2);
            garage.garer(new VoitureV4("45RT34","verso", 11567.56, GRAY));
            // polymorphism
            garage.garer(new VoitureAncienneV4("46RT34","datsun", 11567.56, GRAY, 1945));
            System.out.println(garage.getValeur());
            garage.garer(new VoitureAncienneV4("47RT34","micra", 11567.56, GRAY, 1985));
            System.out.println(garage.getValeur());
        } catch (ExceptionContenance ex) {
      // on émet un rapport! ERROR
      // une clef de traduction de message serait encore mieux
            System.getLogger("com.grandgarage.biz").log(ERROR, "garage plein", ex);
        }
    }

}
package com.grandgarage.biz;

import java.util.Observable;
import java.util.Observer;


import static java.awt.Color.*;
import static java.lang.System.Logger.Level.*;

/**
 * ce code est juste pour une demo
 * dans cette version du "pattern"
 * nous interrogeons l'objet Observable sur son état
 *
 */
public class PanneauGarageC implements Observer {

    public void update(Observable observable, Object sent) {
        GarageC1 garage = (GarageC1) observable ;
        System.out.println(" dépéchons! plus que  " + garage.placesLibres() + " place(s) libre(s)");
    }

    public static void main(String[] args) {
        try {
            int size = 50;
            GarageC1 garage = new GarageC1(size);
            garage.addObserver(new PanneauGarageC());
            garage.garer(new VoitureV4("rhj5","verso", 11567.56, GRAY));
            // polymorphisme
            garage.garer(new VoitureAncienneV4("rhj6","datsun", 11567.56, GRAY, 1945));

            garage.garer(new VoitureAncienneV4("rgh87", "micra", 11567.56, GRAY, 1985));

        } catch (ExceptionContenance ex) {
      // on émet un rapport!
      // une clef de traduction de message serait encore mieux
            System.getLogger("com.grandgarage.biz").log(ERROR, "garage plein", ex);
        }
    }
}
package com.grandgarage.biz;

import java.util.Observable;
import static java.awt.Color.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * En toute logique ce garage devrait prévenir
 * quand on gare un véhicule et quand on en retire un!
 * et donc un type d'objet particulier devrait être
 * passé à notifyObervers pour indiquer si on gare ou si on retire.
 *
 *
 * @author pbamade
 */
public class GarageD extends Observable {

    private VoitureV4[] parking;
    private int nbVoitures;
    private int tailleParking;

    public GarageD(int taille) {
        // on pourrait controler la taille !
        this.tailleParking = taille;
        parking = new VoitureV4[taille];
    }

    public int garer(VoitureV4 voiture) throws ExceptionContenance {
        // le parking est-il plein?
        if (this.nbVoitures >= this.tailleParking) {
            throw new ExceptionContenance(this, voiture);
        }
        for (int index = 0; index < parking.length; index++) {
            if (null == parking[index]) {
                parking[index] = voiture;
                this.nbVoitures++;
                this.setChanged();
                this.notifyObservers(new EvenementGarage(Mouvement.ENTREE, voiture));
                return index;
            }
        }
        return -1; // anomalie!!!!
    // on prévient les parties intéressées

    }

    public int retirer(VoitureV4 voiture) {
        for (int ix = 0; ix < parking.length; ix++) {
            if (voiture.equals(parking[ix])) {
                parking[ix] = null;
                nbVoitures-- ;
                this.setChanged();
                this.notifyObservers(new EvenementGarage(Mouvement.SORTIE, voiture));
                return ix;
            }
        }
        return -1;
    }

    public double getValeur() {
        double res = 0;
        for (int ix = 0; ix < nbVoitures; ix++) {
            if (null != parking[ix]) {
                res += parking[ix].getPrixTTC();
            }
        }
        return res;
    }

    public int placesLibres() {
        return this.tailleParking - this.nbVoitures;
    }
}
package com.grandgarage.biz;

/**
 *
 * @author pbamade
 */
public class EvenementGarage {
    final Mouvement mouvement ;
    final VoitureV4 véhicule ;
    // ici autres informations possibles comme nombre de places libres

    public EvenementGarage(Mouvement mouvement,VoitureV4 véhicule) {
        this.mouvement = mouvement ;
        this.véhicule = véhicule ;
    }

    public String toString() {
        return this. mouvement + " " + this.véhicule ;
    }

}
package com.grandgarage.biz;

/**
 *
 * @author pbamade
 */
public enum Mouvement {
 ENTREE, SORTIE ;
}
package com.grandgarage.biz;

import java.util.Observable;
import java.util.Observer;


import static java.awt.Color.*;
import static java.lang.System.Logger.Level.*;

/**
 * ce code est juste pour une demo
 * dans cette version du "pattern"
 * nous interrogeons l'objet Observable sur son état
 *
 */
public class PanneauGarageD implements Observer {

    public void update(Observable observable, Object sent) {
        GarageD garage = (GarageD) observable ;
        System.out.println(" dépéchons! plus que  " + garage.placesLibres() + " place(s) libre(s)");
    }

    public static void main(String[] args) {
        try {
            int size = 50;
            GarageD garage = new GarageD(size);
            garage.addObserver(new PanneauGarageD());
            garage.garer(new VoitureV4("34RT34", "verso", 11567.56, GRAY));
            // polymorphisme
            VoitureV4 voiture = new VoitureAncienneV4("35RT34","datsun", 11567.56, GRAY, 1945) ;
            garage.garer(voiture);

            garage.garer(new VoitureAncienneV4("33?RY45","micra", 11567.56, GRAY, 1985));

            garage.retirer(voiture) ;

        } catch (ExceptionContenance ex) {
            System.getLogger("com.grandgarage.biz").log(ERROR, "garage plein", ex);
        }
    }
}
package com.grandgarage.biz;

import java.util.Observable;
import java.util.Observer;


import static java.awt.Color.*;
import static java.lang.System.Logger.Level.*;

/**
 ** ce code est juste pour une demo
 * dans cette version du "pattern"
 * nous interrogeons l'objet passé en paramètre du message
 *
 */
public class Panneau2GarageD implements Observer {

    public void update(Observable observable, Object objetConcerné) {
        System.out.println( objetConcerné );
    }

    public static void main(String[] args) {
        try {
            int taille = 50;
          GarageD garage = new GarageD(taille);
            garage.addObserver(new Panneau2GarageD());
            garage.addObserver(new PanneauGarageD());
            garage.garer(new VoitureV4("34RT34", "verso", 11567.56, GRAY));
            // polymorphisme
            VoitureV4 voiture = new VoitureAncienneV4("35RT34","datsun", 11567.56, GRAY, 1945) ;
            garage.garer(voiture);

            garage.garer(new VoitureAncienneV4("33?RY45","micra", 11567.56, GRAY, 1985));

            garage.retirer(voiture) ;
        } catch (ExceptionContenance ex) {
            System.getLogger("com.grandgarage.biz").log(ERROR, "garage plein", ex);
        }
    }
}

Conception avec des types abstraits

package com.grandgarage.biz;

import java.awt.Dimension;

/**
 *
 * @author pbamade
 */
public abstract class Vehicule {
   private final String reférence ;
   private final String modèle ;
   private double prixHT ;
   private final Dimension empattement ;

   public Dimension getEmpattement() {
      return empattement;
   }

   public String getModèle() {
      return modèle;
   }

   public double getPrixHT() {
      return prixHT;
   }

   public String getReférence() {
      return reférence;
   }

   //TODO : controle des préconditions
   public final void setPrixHT(double prixHT) {
      this.prixHT = prixHT;
   }

   //TODO : controles de préconditions
   protected Vehicule(String reférence, String modèle, double prixHT, Dimension empattement) {
      this.reférence = reférence;
      this.modèle = modèle;
      this.prixHT = prixHT;
      this.empattement = empattement;
   }

   public abstract double getTauxTVA() ;

   public double getPrixTTC() {
        return Math.rint(this.prixHT * getTauxTVA());
   }

   // juste pour essayer (pas forcément pertinent)
   public abstract String getType();

   @Override
   public final boolean equals (Object autre){
      if(! (autre instanceof Vehicule)) return false ;
      Vehicule autreV = (Vehicule)autre ;
      return (  (this.getType().equals(autreV.getType())) &&
         (this.reférence.equals(autreV.reférence))) ;
   }


   @Override
   public String toString() {
      //TODO : modifier!
      return this.getType()
         + " " + this.getModèle() ;
   }



}
package com.grandgarage.biz;

/**
 *
 * @author pbamade
 */
public interface EcouteStationnement {
   public void entrée(EvenementStationnement evt) ;
   public void sortie(EvenementStationnement evt) ;
}
package com.grandgarage.biz;

import java.util.EventObject;

/**
 *
 * @author pbamade
 */
public class EvenementStationnement extends EventObject {

   public final Vehicule vehiculeConcerné ;
   public final long estampilleHoraire = System.currentTimeMillis();

   public EvenementStationnement(Stationnement stationnement, Vehicule voiture) {
      super(stationnement) ;
      this.vehiculeConcerné = voiture ;
   }

   /**
    * cas très intéressant:
    * <UL>
    * <LI> covariance sur la méthode <TT>getSource()</TT>
    * <LI> utilisation d'un champ protected de EventObject
    * </UL>
    * @return
    */
   @Override
   public Stationnement getSource() {
      return (Stationnement) this.source ;
   }

   @Override
   public String toString(){
      //TODO : faire mieux!
      return this.vehiculeConcerné.toString();
   }

}
package com.grandgarage.biz;

/**
 *
 * @author pbamade
 */
public interface Stationnement {
   public int garer(Vehicule vehicule) throws ExceptionContenance ;
   public int retirer(Vehicule vehicule) ;
   public int disponibilités() ;
}