:: Enseignements :: Licence :: L3 :: 2022-2023 :: Programmation Objet avec Java ::
[LOGO]

Interface et Entrée/Sortie


Exercice 1 - Blockbuster

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 vrais 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 nom de film, il ne peut y avoir qu'un article au maximum dans le catalogue.
  • 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.

  1. Écrire les types VideoTape et LaserDisc 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));
        

    Attention à ne pas oublier les pré-conditions.
  2. 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

    Quel doit être le type du paramètre de add et le type de retour de lookup ?
    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"));
        
  3. 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
        

    Enfin, discuter du fait que le type des articles doit être scellé ou non ?
    Note : faire en sorte que les noms "LaserDisc" et "VideoTape" soit définis sous forme de constantes pour que le code soit plus lisible.
  4. 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 l'exception 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à.
  5. 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"));
        

    É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.
  6. 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"));
        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"));