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.
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
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 ! System.out.println(catalog.lookup("Jaws")); System.out.println(catalog.lookup("The Cotton Club")); System.out.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(); System.out.println(laserDiscText); // LaserDisc:Jaws System.out.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); System.out.println(laserDisc.equals(laserDisc2)); // true System.out.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")); System.out.println(catalog3.lookup("Jaws")); // LaserDisc:Jaws System.out.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 la 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")); System.out.println(catalog5.lookup("A Fistful of €")); System.out.println(catalog5.lookup("For a Few €s More"));
Note : il existe une classe StandardCharsets qui est une énumération des encodages standard et qui contient l'encodage UTF-8.
Dans cet exercice, on cherche a 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.
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:
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")); System.out.println(catalog7.lookup("Back to the future")); System.out.println(catalog7.lookup("Back to the future part II")); System.out.println(catalog7.lookup("Back to the future part III"));