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

Examen de POO - session 2 2022


Tout document papier est proscrit.
Vous avez le droit de lire le sujet jusqu'au bout, cela vous donnera une bonne idée de là où on veut aller !
La javadoc 17 est disponible à l'URL : https://igm.univ-mlv.fr/~juge/javadoc-17/api/index.html.
Les supports de cours sont disponibles à l'URL : http://igm.univ-mlv.fr/~forax/ens/java-avance/cours/pdf/.

Exercice 1 - Shopping dans les terres du milieu

Tout guerrier sait que les accessoires, épées, boucliers, etc., sont ce qui fait la différence entre la vie et la mort sur un champ de bataille. Il y a donc un marché pour vendre ce genre d'accessoires. Cela tombe bien, vous avez été recruté pour écrire le logiciel permettant de gérer les boutiques d'accessoires des terres du milieu.
Une des particularités des terres du milieu est la monnaie. En effet, au lieu d'avoir une monnaie avec une valeur unique, la monnaie est composée de pièces d'or (gold), de pièces d'argent (silver) et de pièces de cuivre (copper). Et bien sûr, on veut rester souple sur la monnaie car un certain nombre d'achats et de ventes se fait encore en échangeant du tabac (tobacco).
Pour cela, on va utiliser l'enum Coin défini comme ceci
    public enum Coin {
      GOLD, SILVER, COPPER
    }
   
sachant que, dans le futur, on pourrait vouloir ajouter la constante TOBACCO.

Toutes les classes, enum, etc., doivent être déclarées dans le package fr.uge.shop. Les codes fournis dans la suite du sujet doivent être écrits dans la méthode main de la class classe fr.uge.shop.Main.

  1. Dans un premier temps, on va considérer que la monnaie est représentée uniquement par des pièces d'or. Toutefois, pour l'affichage, on va quand même afficher les pièces d'or (g), d'argent (s) et de cuivre (c). Pour l'instant, l'affichage se finira donc toujours par "g 0s 0c".
    On souhaite que la classe Money soit non mutable, que le constructeur sans paramètre crée une monnaie correspondant à 0 pièce d'or et que la méthode add() permette de créer une nouvelle monnaie en ajoutant un nombre strictement positif (non nul) de pièces d'or.
    Écrire la classe Money de telle sorte que le code suivant fonctionne.
         var money1 = new Money();
         var money2 = money1.add(2, Coin.GOLD);
         var money3 = money2.add(3, Coin.GOLD);
         System.out.println(money1);  // 0g 0s 0c
         System.out.println(money2);  // 2g 0s 0c
         System.out.println(money3);  // 5g 0s 0c
        

    Note : si on veut que la classe soit non-mutable, cela veut dire que la valeur ne change pas après la création, donc il vous faut aussi un constructeur privé qui prend en paramètre le nombre de pièces d'or.

  2. On souhaite maintenant définir une constante Money.ZERO qui correspond à 0 pièce d'or et être capable de savoir si deux monnaies ont la même valeur avec la méthode equals.
    Modifier la classe Money de telle sorte que le code suivant fonctionne.
         var money4 = Money.ZERO;
         var money5 = Money.ZERO.add(7, Coin.GOLD);
         System.out.println(money4.equals(new Money()));  // true
         System.out.println(money5.equals(Money.ZERO.add(7, Coin.GOLD)));  // true
         System.out.println(money5.hashCode() == Money.ZERO.add(7, Coin.GOLD).hashCode());  // true
        
    Note : Le code précédent doit continuer à fonctionner sans modification.

  3. On souhaite ajouter une méthode get(coin) qui permet de savoir combien de pièces de chaque type (GOLD, SILVER, COPPER) sont présentes dans une monnaie sachant que, pour l'instant, on ne peut toujours avoir que des pièces d'or.
    Ajouter la méthode get(coin) de telle sorte que le code suivant fonctionne.
         var money6 = Money.ZERO;
         var money7 = Money.ZERO.add(3, Coin.GOLD);
         System.out.println(money6.get(Coin.GOLD));  // 0
         System.out.println(money7.get(Coin.GOLD));  // 3
         System.out.println(money7.get(Coin.COPPER));  // 0
        

  4. On souhaite maintenant gérer la notion de caddie (Caddy) et d'épée (Sword). Une épée est définie par un nom (name, une chaîne de caractères) ainsi qu'un prix (price, de type Money). Un Caddy est vide à la création. On peut y ajouter des épées en utilisant la méthode add. L'affichage d'un caddie affiche la liste des épées dans l'ordre d'appel de add, chaque épée sur une ligne. L'affichage d'une épée affiche son nom et son prix avec le format visible ci-dessous.
    En terme d'implantation, on vous demande que l'affichage d'un Caddy utilise un Stream.
    Faire en sorte que le code suivant fonctionne.
          var sword1 = new Sword("Skullcrusher", Money.ZERO.add(5, Coin.GOLD));
          var caddy1 = new Caddy();
          caddy1.add(sword1);
          System.out.println(caddy1);
            // sword Skullcrusher at 5g 0s 0c
        
    Note : pour l'affichage, il n'y a pas retour à la ligne à la fin de l'affichage. C'est println qui s'en charge.

  5. On souhaite maintenant ajouter la possibilité d'ajouter des boucliers (Shield) à un caddie. On veut de plus qu'il ne soit possible d'ajouter que des épées et des boucliers, et pas d'autres choses, à un caddie. Un bouclier est défini par un booléen (small) qui indique si un bouclier est petit ou normal (true = petit bouclier, false = bouclier normal). En terme d'affichage, si un bouclier est petit l'affichage commence par "small shield" tandis que si le bouclier est normal, l'affichage commence par "shield". En plus du type de bouclier, l'affichage donne aussi le prix d'un bouclier avec le même format que pour le prix d'une épée.
    Modifier votre code de façon à ce que le code suivant fonctionne.
          var sword2 = new Sword("Peacekeeper", Money.ZERO.add(4, Coin.GOLD));
          var shield1 = new Shield(false, Money.ZERO.add(6, Coin.GOLD));
          var caddy2 = new Caddy();
          caddy2.add(sword2);
          caddy2.add(shield1);
          System.out.println(caddy2);
            // sword Peacekeeper at 4g 0s 0c
            // shield at 6g 0s 0c
        

  6. On souhaite maintenant ajouter une méthode last qui renvoie le dernier accessoire (épée ou bouclier) d'un caddie. Comme il peut ne pas y avoir de dernier accessoire, la méthode last revoie un Optional.
    Écrire la méthode last de telle sorte que le code suivant fonctionne.
          var shield2 = new Shield(true, Money.ZERO.add(3, Coin.GOLD));
          var sword3 = new Sword("Gutwrencher", Money.ZERO.add(5, Coin.GOLD));
          var caddy3 = new Caddy();
          caddy3.add(shield2);
          caddy3.add(sword3);
          System.out.println(caddy3.last());
            // Optional[sword Gutwrencher at 5g 0s 0c]
        

  7. On souhaite ajouter une méthode forEach(function) à Caddy, qui prend une fonction en paramètre et appelle cette fonction avec chaque épée et bouclier d'un caddie dans l'ordre d'insertion.
    Écrire le code de la méthode forEach(function) de telle sorte que le code suivant fonctionne.
          var shield3 = new Shield(true, Money.ZERO.add(3, Coin.GOLD));
          var sword4 = new Sword("Deathbringer", Money.ZERO.add(6, Coin.GOLD));
          var caddy4 = new Caddy();
          caddy4.add(shield3);
          caddy4.add(sword4);
          caddy4.forEach(System.out::println);
            // small shield at 3g 0s 0c
            // sword Deathbringer at 6g 0s 0c
        

  8. On veut pouvoir faire la somme des prix des épées et boucliers d'un caddie, mais avant de faire cela, nous allons ajouter une méthode sum dans Money pour faire la somme de deux monnaies.
    Écrire la méthode sum dans Money de telle sorte que le code suivant fonctionne.
           var money8 = Money.ZERO.add(2, Coin.GOLD);
           var money9 = Money.ZERO.add(3, Coin.GOLD);
           var money10 = money8.sum(money9);
           System.out.println(money10);  // 5g 0s 0c
         

  9. On souhaite maintenant ajouter une méthode price à un caddie qui fait la somme des prix des épées et boucliers présents dans le caddie.
    En terme d'implantation, on cherche à utiliser un Stream avec l'algorithme suivant : pour chaque accessoire, on le transforme en son prix, puis on utilise la méthode reduce à deux paramètres pour faire la somme de tous les prix.
         var shield4 = new Shield(true, Money.ZERO.add(3, Coin.GOLD));
         var shield5 = new Shield(true, Money.ZERO.add(3, Coin.GOLD));
         var sword5 = new Sword("Souldrinker", Money.ZERO.add(7, Coin.GOLD));
         var caddy5 = new Caddy();
         caddy5.add(shield4);
         caddy5.add(shield5);
         caddy5.add(sword5);
         System.out.println(caddy5.price());  // 13g 0s 0c
        
    Note : si vous n'arrivez pas à utiliser un Stream, faites sans, mais je vais être déçu.

  10. Dans le but de gérer non seulement les pièces d'or mais également toutes les autres pièces, on va dans un premier temps à ajouter une méthode symbol et une constante COINS à l'enum Coin.
    En Java, les enum sont des sortes de classe, il peuvent donc contenir des champs ou des méthodes. Pour déclarer des membres d'un enum, il faut ajouter un point-virgule (';') à la fin de la liste des constantes. On peut déclarer les champs et méthodes juste après.
         public enum Coin {
           GOLD, SILVER, COPPER;  // le ';' est important
    
           // mettre les champs et les méthodes ici !
         }
        
    La méthode symbol renvoie un caractère qui correspond au type de pièce : 'g' pour GOLD, 's' pour SILVER et 'c' pour COPPER. La constante COINS est une liste non modifiable des constantes de l'enum.
    Écrire la méthode symbol et la constante COINS dans l'enum Coin de telle sorte que le code suivant fonctionne.
         System.out.println(Coin.GOLD.symbol());  // g
         System.out.println(Coin.SILVER.symbol());  // s
         System.out.println(Coin.COPPER.symbol());  // c
         System.out.println(Coin.COINS);  // [GOLD, SILVER, COPPER]
        

    Note : il existe une méthode statique values() définie sur tous les enum et qui renvoie un tableau des constantes définies dans l'enum (dans l'ordre de déclaration).

  11. Enfin, on souhaite fournir une nouvelle implantation de Money qui sait gérer les différents types de pièces. Pour cela, vous allez utiliser une table de hachage (HashMap ou EnumMap à votre convenance) pour associer à un type de pièce (Coin.GOLD, Coin.SILVER, etc) un entier qui indique combien de pièces de ce type existent.
    Commentez l'ancienne implantation de Money et écrire une nouvelle implantation utilisant une table de hachage en interne et telle que le code suivant (et tous les codes précédents) fonctionne.
          var money11 = Money.ZERO.add(1, Coin.SILVER).add(2, Coin.COPPER);
          var money12 = Money.ZERO.add(2, Coin.GOLD).add(1, Coin.COPPER);
          System.out.println(money11);  // 0g 1s 2c
          System.out.println(money12);  // 2g 0s 1c
          System.out.println(money12.get(Coin.GOLD));  // 2
          System.out.println(money12.get(Coin.SILVER));  // 0
          System.out.println(money12.get(Coin.COPPER));  // 1
          System.out.println(money11.sum(money12));  // 2g 1s 3c
        
    Note : si vous vous débrouillez bien, ajouter un nouveau type de pièce comme TOBACCO devrait être aussi simple qu'ajouter la constante dans l'enum Coin, le reste du code devrait s'adapter automatiquement.
    Note 2 : attention à l'ordre d'affichage. Il ne dépend pas de l'ordre dans lequel on ajoute les pièces mais de l'ordre dans lequel les constantes sont définies dans Coin.