-
Dans un premier temps, on cherche à définir un Container.
Un conteneur possède un code BIC (bic), un poids (weight), une valeur entière en kg,
ainsi qu'une destination (destination) sous forme de chaîne de caractères.
Il ne doit pas être possible de créer un conteneur avec des valeurs invalides : le code BIC doit exister,
le poids doit être positif ou nul et la destination doit exister.
Écrire le type Container de telle façon à ce que le code suivant fonctionne:
public static void main(String[] args) {
var container1 = new Container("DSVX 123456 5", 500, "Germany");
System.out.println(container1.bic()); // DSVX 123456 5
System.out.println(container1.weight()); // 500
System.out.println(container1.destination()); // Germany
...
-
On veut maintenant introduire la notion de Manifest, un manifeste contient une liste de conteneurs.
Pour l'instant, un manifeste définit une méthode add(conteneur) qui permet d'ajouter
un conteneur au manifeste.
Il ne doit pas être possible d'ajouter un conteneur null.
Écrire le type Manifest tel que le code suivant fonctionne:
public static void main(String[] args) {
...
var container2 = new Container("MSCU 789012 3", 400, "Italy");
var container3 = new Container("ONEZ 345678 2", 200, "Austria");
var manifest1 = new Manifest();
manifest1.add(container2);
manifest1.add(container3);
}
-
Un porte conteneur comme son nom ne l'indique pas peut aussi transporter des passagers.
Un Passenger est défini par un nom (name) et une destination (destination).
Dans un premier temps, comment définir un Passenger tel que l'on puisse créer un passager.
Puis expliquer comment modifier Manifest pour que l'on puisse enregistrer
aussi bien des conteneurs que des passagers.
Écrire le code de Passenger et modifier le code de Manifest tel que le code
ci-dessous fonctionne.
public static void main(String[] args) {
...
var passenger1 = new Passenger("Nicolas F", "France");
var container4 = new Container("OOCL 098765 0", 350, "England");
var manifest2 = new Manifest();
manifest2.add(passenger1);
manifest2.add(container4);
-
On souhaite ajouter une méthode price à Manifest qui calcul le prix
pour qu'un conteneur ou qu'un passager soit sur le bateau.
-
Le prix pour un passager est 10.
-
Le prix pour un conteneur est le poids du conteneur multiplié par 2.
Ajouter une méthode price à Manifest et faite en sorte que le prix
soit calculés correctement.
System.out.println(manifest2.price()); // 710
-
On souhaite maintenant pouvoir afficher un manifeste.
Afficher un manifeste revient à afficher chaque conteneur/passager sur une ligne,
avec un numéro, 1 pour le premier conteneur/passager, 2 pour le suivant, etc.
Chaque ligne est suivie d'un retour à la ligne, y compris après la dernière ligne.
Pour le formatage exact, vous pouvez regarder l'exemple.
Modifier le type Manifest pour que le code suivant ait le comportement attendu:
public static void main(String[] args) {
...
var manifest3 = new Manifest();
manifest3.add(new Container("OOCL 098765 0", 350, "England"));
manifest3.add(new Passenger("Jane D", "US"));
System.out.println(manifest3);
// 1. OOCL 098765 0 350kg to England
// 2. Jane D to US
}
-
Il arrive que l'on soit obligé de décharger tous les conteneurs liés à une destination
s'il y a des problèmes d'embargo (quand un dictateur se dit qu'il s'offrirait bien une partie
d'un pays voisin par exemple). Dans ce cas, il faut connaitre tous les conteneurs et passagers
liés à cette destination au niveau du manifeste.
Pour prendre en compte cela, on introduit une méthode toDestination(destination)
qui renvoie une liste de tous les containers/passagers allant à ayant la destination
passée en paramètre
Quelle est le type de retour de toDestination(destination) ?
Pourquoi ?
Modifier le code pour introduire cette méthode pour que l'exemple ci-dessous fonctionne:
public static void main(String[] args) {
...
var manifest4 = new Manifest();
manifest4.add(new Container("HAPC 543210 3", 450, "Russia"));
manifest4.add(new Container("BICU 123456 5", 200, "China"));
manifest4.add(new Container("CMAU 432109 6", 125, "Russia"));
manifest4.add(new Passenger("Ana K","Russia"));
var embargoed = manifest4.toDestination("Russia");
System.out.println(embargoed);
// [HAPC 543210 3 450kg to Russia, CMAU 432109 6 125kg to Russia, Ana K to Russia]
-
On souhaite maintenant détecter que le manifeste est valide, c'est-à-dire qu'il n'existe pas
deux passagers ayant le même nom ou deux conteneurs ayant le même cid ou
un passager et un conteneur ayant le même identifiant.
Pour cela, on se propose de créer une méthode checkIsInvalid qui lève une exception
IllegalStateException si le manifeste est invalide.
var manifest5 = new Manifest();
manifest5.add(new Passenger("James Bond", "UK"));
manifest5.add(new Passenger("James Bond", "Iceland"));
manifest5.checkIsInvalid(); // boom !
var manifest6 = new Manifest();
manifest6.add(new Container("HLLY 345678 5", 30, "Slovenia"));
manifest6.add(new Container("HLLY 345678 5", 40, "France"));
manifest6.checkIsInvalid(); // boom !
Quelle est la complexité pire cas, si l'on implante checkIsInvalid en faisant deux boucles
imbriquées sur les passagers ou conteneurs ?
On se propose plutôt d'utiliser l'interface Set, l'implantation HashSet et
la valeur de retour de la méthode add(element).
Décrire en français l'algorithme que l'on doit utiliser ?
Quelle est la complexité pire cas ?
Implanter la méthode checkIsInvalid
-
En fait, avoir une méthode checkIsInvalid est vraiment un mauvais design, le bon design
est de vérifier que l'on ne peut pas créer un manifeste invalide plutôt que de permettre de créer un manifeste
invalide et se poser la question s'il est valide ou non après.
Commenter la méthode checkIsInvalid et modifier la méthode add pour lever l'exception
IllegalStateException dès que l'on essaye d'ajouter un passager ou un conteneur qui
va rendre le manifeste invalide.
Note: l'approche qui consiste à checker que l'on ne peut pas créer un objet invalide plutôt que de vérifier
à postériori qu'un objet est invalide, est référencé en anglais par les 3 mots parse don't validate.
-
[Revision] Pour les plus balèzes,
On met les conteneurs ayant la même 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 manifest7 = new Manifest();
manifest7.add(new Container("BICU 123456 7", 100, "Monaco"));
manifest7.add(new Container("CXSB 987654 9", 200, "Luxembourg"));
manifest7.add(new Container("EYRA 321098 6", 50, "Paris"));
manifest7.add(new Container("DNVN 543210 8", 300, "Monaco"));
manifest7.add(new Passenger("Dimitri From", "Paris"));
System.out.println(manifest7.weightPerDestination());
// {Monaco=400, Luxembourg=200, Paris=50}
}
Note: si vous encore plus balèze, il existe une méthode map.merge() dans l'interface Map
qui peut simplifier votre implantation.