:: Enseignements :: Licence :: L3 :: 2025-2026 :: Programmation Objet avec Java ::
![[LOGO]](http://monge.univ-eiffel.fr/ens/resources/mlv.png) |
Interface et Entrée/Sortie
|
Exercice 1 - Blockbuster
Le but de l'exercice est de continuer à travailler avec des interfaces et des classes et
de manipuler les entrées/sorties.
On souhaite modéliser le catalogue d'un magasin BlockBuster, un magasin
qui loue des cassettes vidéos et des laser discs, sachant que l'on veut être capable
de lire/écrire un catalogue à partir de fichiers.
Oui, on fait de l'archéologie, avant Netflix, les vraies gens allaient dans un magasin
pour louer des films, soit sur des bandes analogiques (des cassettes vidéo) soit
sur disques numériques (des laser discs).
Pour simplifier un peu les choses, on va dire que le magasin a deux sortes d'articles,
des VideoTape et des LaserDisc et que pour un même nom de film,
il ne peut y avoir qu'un article au maximum dans le catalogue avec ce nom de film.
-
Une VideoTape est définie par avec un nom (name) et une durée (duration) de type
java.time.Duration
(un type déjà fourni par le JDK)
-
Un LaserDisc est uniquement défini par un nom.
Un Catalog permet d'ajouter (add) des articles, de chercher (lookup)
un article par son nom, de charger (load des articles à partir d'un fichier et
de sauvegarder (save) les articles du catalogue dans un fichier.
Écrire les types
VideoTape et
LaserDisc dans un package
fr.uge.blockbuster tels que le code suivant fonctionne
var laserDisc = new LaserDisc("Jaws");
var videoTape = new VideoTape("The Cotton Club", Duration.ofMinutes(128));
var videoTape2 = new VideoTape("Mission Impossible", Duration.ofMinutes(110));
On souhaite maintenant écrire un type
Catalog avec une méthode
-
add qui permet d'ajouter une cassette vidéo ou un laser disc.
Attention, cette méthode ne doit pas permettre d'ajouter deux articles ayant le même nom.
-
lookup qui permet de rechercher un article par son nom en temps constant.
Quel doit être le type du paramètre de add et le type de retour de lookup ?
Quel doit être le comportement de add si un article avec le même nom existe déjà dans le catalogue ?
Que doit renvoyer lookup s'il n'y a ni cassette vidéo ni laser disc ayant le nom demandé dans le catalogue ?
Implanter le type Catalog sachant que l'on souhaite que le code suivant fonctionne :
var catalog = new Catalog();
catalog.add(laserDisc);
catalog.add(videoTape);
catalog.add(videoTape2);
// catalog.add(new LaserDisc("Mission Impossible")); // exception !
IO.println(catalog.lookup("Jaws"));
IO.println(catalog.lookup("The Cotton Club"));
IO.println(catalog.lookup("Indiana Jones"));
On veut pouvoir charger et sauvegarder les articles du catalogue dans un fichier,
un article par ligne. Pour cela, on va dans un premier temps écrire deux méthodes, toText et
fromText qui permettent respectivement de renvoyer la forme textuelle d'un article
et de créer un article à partir de sa représentation textuelle.
Pourquoi fromText est-elle une méthode statique alors que toText est une méthode d'instance ?
Le format textuel est composé du type de l'article (
LaserDisc ou
VideoTape)
suivi du nom de l'article et, dans le cas de la cassette vidéo, de la durée en minutes
(il existe une méthode
duration.toMinutes() et une méthode
Duration.ofMinutes()).
Les différentes parties du texte sont séparées par des ":".
Voici un exemple de fichier contenant un laser disc et une cassette vidéo.
LaserDisc:Jaws
VideoTape:The Cotton Club:128
Dans un premier temps, écrire la méthode
toText de telle façon que le code suivant est valide
var laserDiscText = laserDisc.toText();
var videoTapeText = videoTape.toText();
IO.println(laserDiscText); // LaserDisc:Jaws
IO.println(videoTapeText); // VideoTape:The Cotton Club:128
Puis écrire le code de la méthode
fromText sachant qu'il existe une méthode
string.split()
pour séparer un texte suivant un délimiteur et que l'on peut faire un
switch sur des Strings.
Le code suivant devra fonctionner :
var laserDisc2 = Article.fromText(laserDiscText);
var videoTape3 = Article.fromText(videoTapeText);
IO.println(laserDisc.equals(laserDisc2)); // true
IO.println(videoTape.equals(videoTape3)); // true
Note : faire en sorte que les noms "LaserDisc" et "VideoTape" soit définis sous forme de constantes
pour que le code soit plus lisible.
Enfin, discuter du fait que le type des articles doit être scellé ou non ?
En utilisant le pattern matching, écrire videoTapesLessThan(Duration duration)
qui permet de renvoyer une la liste des cassettes vidéo dont la durée est inférieure à la durée passée
en paramètre.
On souhaite maintenant ajouter une méthode save qui permet de sauvegarder les articles
d'un catalogue dans un fichier.
Quelle méthode doit-on utiliser pour créer un écrivain sur un fichier texte à partir d'un Path ?
Comment doit-on faire pour garantir que la ressource système associée est bien libérée ?
Comment doit-on gérer les exceptions d'entrée/sortie ?
Écrire la méthode
save afin que le code suivant fonctionne :
var catalog2 = new Catalog();
catalog2.add(laserDisc);
catalog2.add(videoTape);
catalog2.save(Path.of("catalog.txt"));
Comme Catalog est mutable, on va écrire la méthode load comme une méthode d'instance
et non pas comme une méthode statique. Expliquer quel est l'intérêt.
Écrire la méthode
load dans
Catalog afin que le code suivant fonctionne :
var catalog3 = new Catalog();
catalog3.load(Path.of("catalog.txt"));
IO.println(catalog3.lookup("Jaws")); // LaserDisc:Jaws
IO.println(catalog3.lookup("The Cotton Club")); // VideoTape:The Cotton Club:128
Note : pour load, on ne vous demande pas de vérifier au préalable que le fichier
est bien formé (lignes de la bonne taille, formats numériques corrects, ...).
Vous pouvez laisser filer les exceptions susceptibles de survenir dans ces cas là.
Tout le monde s'est plus ou moins mis d'accord pour que l'UTF-8 soit le format utilisé pour le stockage,
malheureusement, il reste encore plein de Windows XP / Windows 7 qui ne sont pas en UTF8 par défaut.
On va donc ajouter deux surcharges à
load et
save qui prennent en paramètre
l'
encoding. Le code suivant doit fonctionner :
var catalog4 = new Catalog();
catalog4.add(new LaserDisc("A Fistful of €"));
catalog4.add(new VideoTape("For a Few €s More", Duration.ofMinutes(132)));
catalog4.save(Path.of("catalog-windows-1252.txt"), Charset.forName("Windows-1252"));
var catalog5 = new Catalog();
catalog5.load(Path.of("catalog-windows-1252.txt"), Charset.forName("Windows-1252"));
IO.println(catalog5.lookup("A Fistful of €"));
IO.println(catalog5.lookup("For a Few €s More"));
Écrire les deux méthodes et partager le code entre les surcharges pour ne pas dupliquer de code.
Note : il existe une classe StandardCharsets qui est une énumération des encodages
standard et qui contient l'encodage UTF-8.
Exercice 2 - Blockbuster amélioré
Dans cet exercice, on cherche à améliorer la classe Catalog
pour permettre d'avoir plusieurs articles avec le même nom.
On veut modifier la classe Catalog pour qu'elle puisse contenir plusieurs articles avec le même nom.
Pour cela, on va utiliser une HashMap<String, HashSet<Article>> pour stocker les articles du Catalog.
Implanter la classe Catalog avec la modification ci-dessus. La méthode lookup doit renvoyer
un Set<Article> et non plus un seul article.
Exercice 3 - Blockbuster (approfondissement)
Cet exercice permet d'aller plus loin sur les formats binaires.
De façon optionnelle, pour les plus balèzes, on veut ajouter le support des fichiers binaires
en ajoutant deux méthodes saveInBinary et loadInBinary qui permettent respectivement
de sauver un fichier en binaire et de charger un fichier binaire.
Le format binaire utilisé est:
-
un entier 32 bits indiquant le nombre d'articles,
-
pour chaque article, son type, un entier 8 bits (1 pour VideoTape, 2 pour LaserDisc),
son nom en modified UTF8 et dans le cas de VideoTape un entier long 64 bits avec le nombre de minutes.
Il existe des classes
DataInputStream et
DataOutputStream que l'on peut construire
respectivement sur
InputStream et
OutputStream et qui sont capables de lire/écrire
un entier 8 bits, un entier 64 bits ou une chaîne au format
modified UTF8.
Écrire les méthodes
saveInBinary et
loadInBinary et vérifier que le code
suivant fonctionne.
var catalog6 = new Catalog();
catalog6.add(new VideoTape("Back to the future", Duration.ofMinutes(116)));
catalog6.add(new LaserDisc("Back to the future part II"));
catalog6.add(new LaserDisc("Back to the future part III"));
catalog6.saveInBinary(Path.of("catalog.binary"));
var catalog7 = new Catalog();
catalog7.loadFromBinary(Path.of("catalog.binary"));
IO.println(catalog7.lookup("Back to the future"));
IO.println(catalog7.lookup("Back to the future part II"));
IO.println(catalog7.lookup("Back to the future part III"));
© Université de Marne-la-Vallée