5. Exercices sur un système de gestion de véhicules¶
Voici un exercice sur un système de gestion de véhicules, basé en partie sur l’exemple précédent.
Partie 1¶
- Crée un projet dans IntelliJ nommé
Vehicules
. - Crée un package nommé
vehicules
dans le dossiersrc
. - Crée toutes les classes données dans le diagramme de classes suivant.
- Teste ton code avec le
main
qui suit le diagramme.- Pour tester tes classes avec le code du
Main
, tu devras ajouter au moins un constructeur par classe.
- Pour tester tes classes avec le code du
- Ajoutes des getters et des setters pour tes classes.
Diagramme de classes¶
Source PlantUML
@startuml
skinparam classAttributeIconSize 0
class Vehicule {
- nom: String
- vitesseMax: int
+ toString(): String
}
class VehiculeNonMotorise {
- typePropulsion: String
+ toString(): String
}
class VehiculeMotorise {
- puissance: int
+ toString(): String
}
class Velo {
- nombreDeVitesses: int
+ toString(): String
}
class Caleche {
- typeAnimal: String
- nombreDeChevaux: int
+ toString(): String
}
class PatinsARoulettes {
- nombreDeRoues: int
+ toString(): String
}
class Moto {
- cylindree: int
+ toString(): String
}
class Voiture {
- nombreDePortes: int
+ toString(): String
}
class Camion {
- capaciteDeCharge: int
+ toString(): String
}
Vehicule <|-- VehiculeNonMotorise
Vehicule <|-- VehiculeMotorise
VehiculeNonMotorise <|-- Velo
VehiculeNonMotorise <|-- Caleche
VehiculeNonMotorise <|-- PatinsARoulettes
VehiculeMotorise <|-- Moto
VehiculeMotorise <|-- Voiture
VehiculeMotorise <|-- Camion
@enduml
Main.java
¶
import vehicules.*;
public class Main {
public static void main(String[] args) {
Velo velo = new Velo("VTT", 40, 21);
Caleche caleche = new Caleche("vehicules.Caleche de luxe", 10, "Cheval", 2);
PatinsARoulettes patins = new PatinsARoulettes("Patins de vitesse", 30, 4);
Moto moto = new Moto("Honda CB500F", 180, 47, 500);
Voiture voiture = new Voiture("Peugeot 308", 220, 130, 5);
Camion camion = new Camion("Mercedes-Benz Actros", 120, 500, 20);
System.out.println("Détails du vélo:\n" + velo);
System.out.println("\nDétails de la calèche:\n" + caleche);
System.out.println("\nDétails des patins à roulettes:\n" + patins);
System.out.println("\nDétails de la moto:\n" + moto);
System.out.println("\nDétails de la voiture:\n" + voiture);
System.out.println("\nDétails du camion:\n" + camion);
}
}
Partie 2¶
Faites les modifications suivantes au projet.
Attributs à Ajouter¶
- Couleur : Ajouter un attribut
couleur
à la classeVehicule
. - Poids : Ajouter un attribut
poids
à la classeVehicule
. - Type de Carburant : Ajouter un attribut
typeCarburant
à la classeVehiculeMotorise
. - Nombre de Passagers : Ajouter un attribut
nombreDePassagers
à la classeVehiculeMotorise
. - Vitesse Courante : Ajouter un attribut
vitesseCourante
à la classeVehicule
.
Ajouter également les getters et setters correspondants.
Méthodes à Ajouter¶
- Méthode
demarrer()
: Ajouter une méthodedemarrer()
à la classeVehiculeMotorise
pour simuler le démarrage du moteur. - Méthode
accelerer(double delta)
: Ajouter une méthodeaccelerer()
à la classeVehicule
pour simuler l’accélération. - Méthode
freiner(double delta)
: Ajouter une méthodefreiner()
à la classeVehicule
pour simuler le freinage.
Le but de cet exercice est de comprendre l’héritage et le polymorphisme. Le but n’est pas de faire une simulation exacte
du comportement des véhicules. Vous pouvez par exemple placer des println
dans les méthodes, et utiliser le paramètre
double delta
pour ajouter ou enlever à la vitesse courante, et ne pas calculer l’accélération avec les formules
physiques correctes.
Utilisation du Polymorphisme¶
- Créer un tableau ou une liste de
Vehicule
et y ajouter des instances de différentes sous-classes. - Itérer sur ce tableau pour appeler les méthodes ajoutées (
demarrer()
,accelerer()
,freiner()
,calculerPrix()
) pour démontrer le polymorphisme.
Diagramme de classes¶
Source PlantUML
@startuml
skinparam classAttributeIconSize 0
class Vehicule {
- nom: String
- vitesseMax: int
- vitesseCourante: int
- couleur: String
- poids: int
+ toString(): String
+ accelerer(double delta): void
+ freiner(double delta): void
}
class VehiculeNonMotorise {
- typePropulsion: String
+ toString(): String
}
class VehiculeMotorise {
- puissance: int
- typeCarburant: String
- nombreDePassagers: int
+ toString(): String
+ demarrer(): void
}
class Velo {
- nombreDeVitesses: int
+ toString(): String
}
class Caleche {
- typeAnimal: String
- nombreDeChevaux: int
+ toString(): String
}
class PatinsARoulettes {
- nombreDeRoues: int
+ toString(): String
}
class Moto {
- cylindree: int
+ toString(): String
+ demarrer(): void
}
class Voiture {
- nombreDePortes: int
+ toString(): String
+ demarrer(): void
}
class Camion {
- capaciteDeCharge: int
+ toString(): String
+ demarrer(): void
}
Vehicule <|-- VehiculeNonMotorise
Vehicule <|-- VehiculeMotorise
VehiculeNonMotorise <|-- Velo
VehiculeNonMotorise <|-- Caleche
VehiculeNonMotorise <|-- PatinsARoulettes
VehiculeMotorise <|-- Moto
VehiculeMotorise <|-- Voiture
VehiculeMotorise <|-- Camion
@enduml
Partie 3¶
La partie 3 ajoute un gestionnaire de véhicules qui gère tous les types de véhicules dans une seule liste
private List<Vehicule> vehicules;
. La classe com.google.gson.Gson
est utilisé pour exporter/importer la liste en
format JSON. Mais pour bien charger les véhicules sauvegardés dans la liste, il faut également utiliser
com.google.gson.GsonBuilder
et définir un JsonDeserializer
pour charger les véhicules dans la bonne sous-classe de
Vehicule
, sinon tous les véhicules seront créés directement comme des instances de Vehicule
, et non pas dans les
bonnes sous-classes (Moto
, Velo
, …).
L’attribut type
a été ajouté à la classe Vehicule
, et est initialisé au nom de la classe dans le constructeur avec
this.type = getClass().getSimpleName();
.
Par exemple, le vélo créé de cette façon
va être sérialisé de cette façon :
{
"nombreDeVitesses": 21,
"typePropulsion": "Pédalage",
"type": "Velo",
"nom": "VTT",
"vitesseMax": 40,
"vitesseCourante": 0,
"couleur": "Bleu",
"poids": 15
}
Si l’attribut type
n’était pas présent dans la version JSON de l’objet, comment pourrait-on savoir que l’objet n’est
pas seulement un Vehicule
, mais un Velo
, qui est une sous-classe de Vehicule
? On pourrait remarquer que
l’attribut nombreDeVitesses
est défini uniquement dans Velo
, donc on pourrait en déduire que c’est vraiment un
Velo
. Mais que fera-t-on pour les autres sous-classes de Vehicule
? Il faudra identifier des cas particuliers pour
les différencier, ce qui ne sera pas toujours possible en général. Et même si c’était possible, le code obtenu dans le
Deserializer
sera plus complexe, avec plusieurs cas particuliers à gérer. En ajoutant un attribut type qui est géré
automatiquement dans le constructeur, le code du Deserializer
sera simplifié, et l’ajout de nouvelles sous-classes
sera également simplifié grandement.
Classe VehiculeDeserializer
¶
Objectif du Code¶
Ce code est un désérialiseur personnalisé pour Gson
qui permet de convertir un JSON en objets Java de type
Vehicule
(et ses sous-classes comme Velo
, Voiture
, etc.). Il gère le polymorphisme en se basant sur un champ
"type"
dans le JSON pour déterminer la classe concrète à instancier.
1. Déclaration de la Classe¶
public class VehiculeDeserializer implements JsonDeserializer {
private static final String CLASS_PROPERTY_NAME = "type";
// ...
}
JsonDeserializer
: Indique que cette classe désérialise des objets de typeVehicule
.CLASS_PROPERTY_NAME
: Le nom du champ dans le JSON qui contient le type de véhicule (par défaut"type"
).
2. Méthode deserialize()
¶
@Override
public Vehicule deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
String type = jsonObject.get(CLASS_PROPERTY_NAME).getAsString();
try {
Class clazz = Class.forName("vehicules." + type);
return context.deserialize(json, clazz);
} catch (ClassNotFoundException e) {
throw new JsonParseException("Classe non trouvée: " + type, e);
}
}
Étapes :¶
-
Extraction du type :
- Récupère la valeur du champ
"type"
dans le JSON (ex :"Velo"
,"Voiture"
).
- Récupère la valeur du champ
-
Chargement de la classe :
- Construit le nom complet de la classe (ex:
vehicules.Velo
). - Suppose que toutes les sous-classes de
Vehicule
sont dans le packagevehicules
.
- Construit le nom complet de la classe (ex:
-
Désérialisation :
- Utilise Gson pour convertir le JSON en objet de la classe trouvée.
Exemple¶
JSON d’Entrée¶
{
"type": "Velo",
"nom": "VTT",
"vitesseMax": 40,
"couleur": "Bleu",
"poids": 15,
"nombreDeVitesses": 21
}
Comportement :¶
- Le champ
"type": "Velo"
est détecté. - La classe
vehicules.Velo
est chargée. Gson
crée un objetVelo
avec les attributs du JSON.
Points Clés à Comprendre¶
1. Polymorphisme :¶
- Permet de désérialiser des sous-classes de
Vehicule
sans connaître leur type à l’avance. - Nécessite que le JSON contienne un champ
"type"
pour indiquer la classe cible.
2. Réflexion Java :¶
- Utilise
Class.forName()
pour charger dynamiquement une classe à partir de son nom. - Si la classe n’existe pas, une exception
ClassNotFoundException
est levée.
3. Gestion des Erreurs :¶
- Si le champ
"type"
est manquant dans le JSON :NullPointerException
. - Si le nom de classe est incorrect :
JsonParseException
.
Problèmes Potentiels¶
1. Package Invalide :¶
- Si les sous-classes ne sont pas dans
vehicules
, ajustez le code :
2. Sérialisation :¶
- Pour que cela fonctionne, le JSON doit être généré avec le champ
"type"
. Utilisez un sérialiseur personnalisé ou ajoutez manuellement le champ.
3. Sécurité :¶
- La réflexion (
Class.forName()
) peut être risquée si le champ"type"
provient d’une source non fiable (risque d’injection de code).
Citations
- [1] https://www.finra.org/about/technology/blog/how-to-serialize-deserialize-interfaces-in-java-using-gson
- [2] https://stackoverflow.com/questions/21767485/gson-deserialization-to-specific-object-type-based-on-field-value
- [3] https://www.jmdoudoux.fr/java/dej/chap-gson.htm
- [4] https://github.com/google/gson
- [5] https://jmdoudoux.developpez.com/cours/developpons/java/chap-gson.php
- [6] https://www.baeldung.com/gson-list
- [7] https://annycedavis.com/2014/10/android-custom-gson-deserialization-w.html
- [8] http://google.github.io/gson/UserGuide.html
Classe GestionnaireVehicules
¶
Dans le constructeur du gestionnaire de véhicules, il faut modifier la création de l’instance de Gson
pour utiliser le
VehiculeDeserializer
.
this.gson = new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(Vehicule.class, new VehiculeDeserializer())
.create();
La méthode setPrettyPrinting()
est utilisée pour faciliter la lecture du fichier JSON par un humain.
Info IA
Page rédigée en partie avec l’aide d’un assistant IA, principalement à l’aide de Perplexity AI, avec le LLM Claude 3.5 Sonnet. L’IA a été utilisée pour générer des explications, des exemples et/ou des suggestions de structure. Toutes les informations ont été vérifiées, éditées et complétées par l’auteur.