:: Enseignements :: Licence :: L3 :: 2021-2022 :: Programmation Objet avec Java ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) |
TP noté Java - 2021
|
Le but de ce TP noté est d'écrire quelques classes permettant
de vérifier que vous maîtrisez la mise en œuvre des mécanismes de
base pour la création de classes et d'objets, de délégation
de responsabilité, de la création d'interface, de sous-typage et
de la liaison tardive ainsi que l'utilisation de collections élémentaires
pour la réalisation de petits algorithmes simples en Java.
À lire absolument
Tout ce que vous devez rendre devra obligatoirement être placé dans
le répertoire EXAM à la racine de votre compte ; sinon, ce n'est pas récupéré
et vous aurez 0.
Vous vous attacherez à toujours mettre les champs avec des modificateurs
d'accessibilité les plus restreints possibles, à factoriser le code au maximum,
de sorte à éviter autant que possible toute duplication de code et à ne pas
ajouter de méthodes qui ne sont pas nécessaires par rapport à ce qui est demandé
dans le sujet.
Vous écrirez toutes les classes de ce TP noté dans un package nommé
fr.uge.manifest. Vous devez tester toutes les méthodes demandées
et vous écrirez tous vos tests dans la classe Main
du package fr.uge.manifest.main.
Attention, il est très important de respecter les noms des paquetages, des
classes, des champs, des méthodes, l'ordre des paramètres que l'on vous demande :
une partie de la correction est automatique.
Exercice 1 - Manifeste d'un porte conteneur
Un porte-conteneur (container en Anglais) est un bateau qui, comme son nom l'indique,
transporte des conteneurs d'un port à l'autre. Chaque porte-conteneur possède un manifeste
(manifest), qui est un document papier contenant une liste de l'ensemble des conteneurs
qu'il transporte.
Le but de ce TP est de modéliser ce document papier.
-
Dans un premier temps, on cherche à définir un Container.
Un conteneur possède une destination sous forme de chaîne de caractères ainsi qu'un poids
(weight en Anglais) qui est une valeur entière.
Il ne doit pas être possible de créer un conteneur avec des valeurs invalides : la destination doit exister
et le poids doit être positif ou nul.
Écrire le type Container de telle façon à ce que le code suivant fonctionne :
public static void main(String[] args) {
var container = new Container("Germany", 500);
System.out.println(container.destination()); // Germany
System.out.println(container.weight()); // 500
}
-
On veut maintenant introduire la notion de Manifest, un manifeste contient une liste de conteneurs.
Pour l'instant, un manifeste définit deux méthodes
-
add(conteneur) qui permet d'ajouter un conteneur au manifeste.
Il ne doit pas être possible d'ajouter un conteneur null.
-
weight() qui renvoie la somme des poids des conteneurs du manifeste
Écrire le type Manifest tel que le code suivant fonctionne :
public static void main(String[] args) {
...
var container2 = new Container("Italy", 400);
var container3 = new Container("Austria", 200);
var manifest = new Manifest();
manifest.add(container2);
manifest.add(container3);
System.out.println(manifest.weight()); // 600
}
-
On souhaite maintenant pouvoir afficher un manifeste.
Afficher un manifeste revient à afficher chaque conteneur sur une ligne,
avec un numéro, 1 pour le premier conteneur, 2 pour le suivant, etc, suivi
de la destination du conteneur ainsi que de son poids.
Le formatage exact pour une ligne est :
[numéro]. [destination] [poids]
suivi d'un retour à la ligne (y compris après la dernière ligne).
Modifier le type Manifest pour que le code suivant ait le comportement attendu :
public static void main(String[] args) {
...
var container4 = new Container("Spain", 250);
var container5 = new Container("Swiss", 200);
var manifest2 = new Manifest();
manifest2.add(container4);
manifest2.add(container5);
System.out.println(manifest2);
// 1. Spain 250
// 2. Swiss 200
}
-
Sur le manifeste, un conteneur peut avoir un tampon (stamp) en forme d'étoile (star)
pour indiquer que le conteneur a son prix qui sera réduit. On ne s'intéresse pas pour l'instant
à la façon dont on calcule le prix d'un conteneur, on veut juste modéliser le fait que
certains conteneurs ont un tampon et pas les autres.
Pour cela, on va créer un type StarStamp qui représente un tampon sous forme d'étoile
et modifier le code pour permettre de créer des containers avec un tampon sous forme d'étoile.
On veut aussi que l'ancien code qui créé des conteneurs sans tampon puisse continuer à
fonctionner.
Lorsque l'on affiche un conteneur qui a un tampon sous forme d'étoile, une étoile apparait
à la fin de la ligne d'affichage.
Modifier le type Container pour que le code ci-dessous
et les codes précédemment écrits dans le main fonctionnent.
public static void main(String[] args) {
...
var stamp = new StarStamp();
var container6 = new Container("France", 550, stamp);
var manifest3 = new Manifest();
manifest3.add(container6);
System.out.println(manifest3);
// 1. France 550 *
-
On souhaite introduire un nouveau type de tampon, les tampons sous forme de plus (+).
Dans le futur, on pourrait même vouloir introduire encore de nouvelles sortes de tampon.
Un tampon sous forme de plus contient une catégorie (category) qui est un nombre
entre 1 et 5 (les deux compris) et qui indique le nombre de plus sur le tampon.
Il ne doit pas être possible de créer des tampons sous forme de plus avec une valeur
de catégorie invalide.
Lorsque l'on affiche un conteneur qui a un tampon sous forme de plus, autant de plus que
la catégorie apparaissent à la fin de la ligne d'affichage : par exemple, si la catégorie est
2, deux '+' sont affichés.
Créer un type PlusStamp qui représente les tampons sous forme de plus et faire en sorte
que l'on puisse créer des conteneurs soit avec des tampons sous forme d'étoile, soit avec des
tampons sous forme de plus, soit sans tampons.
Vérifier que le code suivant fonctionne. Bien sûr le reste du main doit continuer
à fonctionner :
public static void main(String[] args) {
...
var container7 = new Container("Venezuela", 150, new PlusStamp(1));
var container8 = new Container("Liberia", 200, new StarStamp());
var container9 = new Container("Alaska", 250, new PlusStamp(3));
var manifest4 = new Manifest();
manifest4.add(container7);
manifest4.add(container8);
manifest4.add(container9);
System.out.println(manifest4);
// 1. Venezuela 150 +
// 2. Liberia 200 *
// 3. Alaska 250 +++
}
-
Il arrive que l'on soit obligé de décharger tous les conteneurs liés à une destination
s'il y a des problèmes de frais de douane. Dans ce cas, il faut aussi supprimer tous les conteneurs
liés à cette destination au niveau du manifeste.
Pour prendre en compte cela, on introduit une méthode removeAllContainersFrom(destination)
qui supprime tous les conteneurs liés à une destination. S'il n'y a pas de conteneur pour cette
destination, on ne fait rien.
Modifier le code pour introduire cette méthode pour que l'exemple ci-dessous fonctionne :
public static void main(String[] args) {
...
var container10 = new Container("Portugal", 450);
var container11 = new Container("China", 200);
var container12 = new Container("Portugal", 125);
var manifest5 = new Manifest();
manifest5.add(container10);
manifest5.add(container11);
manifest5.add(container12);
manifest5.removeAllContainersFrom("Portugal");
System.out.println(manifest5);
// 1. China 200
}
-
On souhaite maintenant pouvoir calculer le prix qu'un client va payer pour mettre
ses conteneurs sur le porte-conteneur. Le prix dépend de la destination de chaque conteneur.
Pour cela, on va ajouter une méthode price qui prend comme premier paramètre un
dictionnaire qui associe un prix à chaque destination, et comme second paramètre
le prix à utiliser si la destination n'a pas de prix associé dans le dictionnaire.
Par exemple, avec le code suivant, le premier conteneur a pour destination "Scotland", et le dictionnaire
indique que ce conteneur a un prix de 40. Pour le second conteneur, la destination est "Ireland",
et le prix associé est de 50. Enfin, pour le dernier conteneur, "Russia" n’apparaît pas dans le dictionnaire,
donc le prix est celui par défaut, c'est-à-dire 100 (le deuxième paramètre).
Le prix total est 40 + 50 + 100 = 190.
public static void main(String[] args) {
...
var container13 = new Container("Scotland", 200);
var container14 = new Container("Ireland", 100);
var container15 = new Container("Russia", 200);
var manifest6 = new Manifest();
manifest6.add(container13);
manifest6.add(container14);
manifest6.add(container15);
var price = manifest6.price(Map.of("Scotland", 40, "Ireland", 50), 100);
System.out.println(price); // 190
}
Note : attention à bien vérifier que le dictionnaire pris en paramètre n'a que des valeurs valides !
-
En fait, le calcul du prix est un peu plus compliqué, car il ne dépend pas uniquement de la destination
mais aussi des tampons. Dans le cas où l'on a un tampon sous forme d'étoile, il y a une réduction
sur le prix de 200 (uniquement sur le prix du conteneur, un conteneur ne peut pas avoir un prix négatif !).
Dans le cas où l'on a un tampon sous forme de plus, le conteneur à une réduction de prix de 10% par plus
(par exemple ++++ correspond à une réduction de 40%).
Dans l'exemple ci-dessous, le "Laos" ne fait pas partie des destinations du dictionnaire donc le prix est
400, le "Vietnam" non plus n'est pas dans le dictionnaire, donc le prix est 400 mais comme
il y a un tampon sous forme d'étoile, le prix est 400 - 200 = 200, pour la "Thailand" a un prix de
100 dans le dictionnaire mais comme il y a un tampon ++, le prix est réduit de 20%,
donc 100 - 100 * 20 / 100 = 80. Le prix total est 400 + 200 + 80 = 680.
public static void main(String[] args) {
...
var container16 = new Container("Laos", 120);
var container17 = new Container("Vietnam", 120, new StarStamp());
var container18 = new Container("Thailand", 120, new PlusStamp(2));
var manifest7 = new Manifest();
manifest7.add(container16);
manifest7.add(container17);
manifest7.add(container18);
var price7 = manifest7.price(Map.of("Thailand", 100), 400);
System.out.println(price7); // 680
}
-
On met les conteneurs ayant la destination au même endroit sur le porte-conteneur, et si
un porte-conteneur est mal équilibré il a une fâcheuse tendance à se retourner. Donc, pour aider au placement
des conteneurs, il doit être possible de fournir un dictionnaire qui, pour chaque destination, indique
le poids de l'ensemble des conteneurs liés à cette destination.
Pour cela, écrire une méthode weightPerDestination qui, pour un manifeste donné, renvoie un dictionnaire
qui indique le poids des conteneurs pour chaque destination.
Par exemple, avec le code ci-dessous, il y a deux conteneurs qui ont comme destination "Monaco", avec un
poids combiné de 100 + 300 = 400, tandis que "Luxembourg" a un seul conteneur de poids 200.
public static void main(String[] args) {
...
var container19 = new Container("Monaco", 100);
var container20 = new Container("Luxembourg", 200);
var container21 = new Container("Monaco", 300);
var manifest8 = new Manifest();
manifest8.add(container19);
manifest8.add(container20);
manifest8.add(container21);
System.out.println(manifest8.weightPerDestination());
// {Monaco=400, Luxembourg=200}
}
© Université de Marne-la-Vallée