:: Enseignements :: ESIPE :: E3INFO :: 2021-2022 :: Programmation Objet avec Java ::
[LOGO]

TP noté Java


Le but de ce TP noté est d'écrire quelques classes permettant de vérifier que vous maitrisez la mise en oeuvre des mécanismes de base pour la création de classes et d'objets, de composition, de délégation et de responsabilité, de sous-typage par interface, ainsi que l'utilisation de collections élémentaires pour la réalisation de petits algorithmes simples en Java.

À lire absolument

Tout ce que vous devez rendre devra obligatoirement être placé dans le répertoire EXAM à la racine de votre compte ; sinon, ce n'est pas récupéré et vous aurez 0.

Tout document papier est proscrit.
La javadoc 17 est https://igm.univ-mlv.fr/~juge/javadoc-17/api/index.html.
Les seuls documents électroniques autorisés sont les supports de cours à l'url http://igm.univ-mlv.fr/~forax/ens/java-avance/cours/pdf/.

Vous vous attacherez à toujours mettre les champs avec des modificateurs d'accessibilité les plus restreints possibles, à factoriser le code au maximum, de sorte à éviter autant que possible toute duplication de code et à ne pas ajouter de méthodes qui ne sont pas nécessaire par rapport à ce qui est demandé dans le sujet.

Vous écrirez toutes les classes de ce TP noté dans un package nommé fr.uge.starstruck. Vous devez tester toutes les méthodes demandées et vous écrirez tous vos tests dans la classe fr.uge.starstruck.main.Main d'un sous-package.
Attention, il est très important de respecter les noms des paquetages, des classes, des champs et des méthodes que l'on vous demande : une partie de la correction est automatique.

Exercice 1 - StarStruck, un café servi par une star

On veut développer le système d'information d'une nouvelle chaîne de cafés nommé StarStruck servant des cafés et des patisseries.
Le concept, est que de temps en temps, des stars viennent servir des cafés à la place des employés habituels. Le slogan est bien sûr "Being starstruck in a StarStruck".
On va modéliser une petite partie du système d'information d'un café et définir des types tel que Coffee qui repésente un café (la boisson pas l'étalissement), Employee qui représente un employé ou Invoice qui représente une facture remise au client une fois les achats faits.

  1. Dans un premier temps, on souhaite écrire le code de Coffee qui représente un café avec un pays d'origine (country) de type chaîne de caractères et un booléen indiquant si le café contient du lait ou pas (true si le café contient du lait).
    On veut de plus, pouvoir afficher un café tel que l'exemple ci-dessous fonctionne. C'est à dire, afficher "coffee" suivi du pays d'origine ainsi que "with milk" si le café contient du lait.
    Note: attention à bien respecter l'affichage et à ne pas ajouter des espaces supplémentaires sinon la correction automatique va rejeter votre code.
    Note2: attention à ne pas permettre de créer des cafés invalides !
      // dans le package fr.uge.startstruck.main
      // dans la classe Main
    
      public static void main(String[] args) {
        var coffee1 = new Coffee("Columbian", false);
        System.out.println(coffee1);  // coffee Columbian
    
        var coffee2 = new Coffee("Peruvian", true);
        System.out.println(coffee2);  // coffee Peruvian with milk
      }
        

    Ecrire le code de Coffee tel que le code ci-dessus fonction et affiche les bonnes valeurs.
  2. On souhaite mantenant modéliser une facture Invoice qui va récapituler les cafés achetés par un client ainsi que le prix total de vente. Pour cela, nous avons aussi besoin de la notion d'employé (Employee), défini juste par un nom (name), car une facture est créée par un employé.
    La méthode add sur Invoice permet d'ajouter un café à la facture. La méthode price sur Invoice renvoie le prix total.
    Le prix d'un café est 1 (Euro) et le lait coûte un supplément de 2. Par exemple, avec les deux cafés précédenmment définie, le premier vaut 1 et le second 3, donc une facture avec ces deux cafés a un prix total de 4 (1 + 3).
    Ecrire les types Invoice, Employee tel que le code suivant fonctionne et affiche les bonnes valeurs.
    Note: n'oublier pas qu'il ne doit pas être possible de créer des objets invalides !
      public static void main(String[] args) {
        ...
        var john = new Employee("John");
        var invoice1 = new Invoice(john);
        invoice1.add(coffee1);
        invoice1.add(coffee2);
        System.out.println(invoice1.price());  // 4
      }
        
  3. On souhaite maintenant pouvoir afficher une facture. L'affichage doit indiquer l'employé ayant créé la facture (précédé par "served by") puis l'ensemble des cafés, un par ligne et enfin le prix total de la facture. Enfin, on séparera les 3 parties de la facture par des "---".
    Ecrire le code tel que le code suivant fonctionne et affiche les bonnes valeurs.
    Note: les retours à la ligne sont des '\n' même sous Windows.
    Note2: attention à ce que le format soit complètement (100%) correcte, sinon la correction automatique va rejeter votre code comme invalide.
      public static void main(String[] args) {
        ...
        System.out.println(invoice1);
        // served by John
        // ---
        // coffee Columbian
        // coffee Peruvian with milk
        // ---
        // price: 4
      }
        
  4. On vaut maintenant qu'une facture puisse être établie non pas un employé mais par une star (Star). Une star est définie par un nom (name), un valeur entre 1 et 10 compris (starometer) indiquant la renomée de la star et un pays d'origine (contry).
    Définir Star et modifier le code précédemment écrit pour que l'exemple ci-dessous fonctionne et affiche les bonnes valeurs. Le prix d'un Invoice fait par une Star est multiplié par le nombre d'étoiles de la Star.
      public static void main(String[] args) {
        ...
        var georges = new Star("Georges", 8, "USA");
        var invoice2 = new Invoice(georges);
        invoice2.add(coffee1);
        invoice2.add(coffee2);
        System.out.println(invoice2.price());  // 32 = 4 * 8
      }
        

    Note: attention, les exemples de code dans le main doivent continuer à fonctionner.
  5. Si on affiche une facture créée par une star, la première ligne doit être, "served by" suivi du nom de la star suivi d'autant d'étoiles que sa valeur starometer. Par exemple, Georges a 8 comme valeur de starometer, donc 8 étoiles seront affichées à la suite de son nom.
    Modifier votre code tel que le code suivant fonctionne et affiche les bonnes valeurs.
      public static void main(String[] args) {
        ...  
        System.out.println(invoice2);
        // served by Georges ********
        // ---
        // coffee Columbian
        // coffee Peruvian with milk
        // ---
        // price: 32
      }
        
  6. En fait, vendre juste des cafés n'est pas assez rentable, il faut se diversifier. Après une étude de marché poussée (aller visiter les concurrents), vous decidez de vendre aussi des gateaux (Cake) définies par un nom (name) et une liste d'ingrédients, une liste de chaines de caractères (ingredients).
    : Un gateau possède au moins un ingrédient et il ne doit pas être possible de modifier les ingrédients après la création d'un gateau (même si l'on modifie la liste après l'appel au constructeur).
    Définir Cake et faites en sorte que le code suivant fonctionne et affiche les bonnes valeurs.
      public static void main(String[] args) {
        ...
        var cake1 = new Cake("Brownie", List.of("sugar", "chocolate", "milk"));
        System.out.println(cake1);  // Brownie with milk, chocolate, sugar
    
        var cake2 = new Cake("Kouign Amann", List.of("butter"));
        System.out.println(cake2);  // Kouign Amann with butter
      }
        
  7. Bien sûr, il faut que l'on puisse facturer des gateaux sachant Le prix d'un gateau est le nombre d'ingrédients.
    Modifier votre code pour que l'exemple suivant fonctionne et affiche les bonnes valeurs.
      public static void main(String[] args) {
        ... 
        var coffee3 = new Coffee("Kenyan", false);
        var invoice3 = new Invoice(john);
        invoice3.add(cake1);
        invoice3.add(cake2);
        invoice3.add(coffee3);
        System.out.println(invoice3);
        // served by John
        // ---
        // Brownie with sugar, chocolate, milk
        // Kouign Amann with butter
        // coffee Kenyan
        // ---
        // price: 5
        
  8. Certaines personnes sont intolérantes au lait de vache, il faut donc être capable de rétirer d'une facture les cafés et les gateaux contenant du lait. Pour cela, on souhaite ajouter une méthode removeMilkRelatedProducts qui retire les cafés et les gateaux contenant du lait d'une facture.
    Attention, pour les ingrédients d'un gateau, "milk" peut apparaitre en majuscule, en miniuscule ou alors avec une combinaison de majuscules et de minuscules.
    Ajouter la méthode removeMilkRelatedProducts et vérifier que le code ci-dessous fonctionne et affiche les bonnes valeurs.
      public static void main(String[] args) {
        ...
        var invoice4 = new Invoice(john);
        invoice4.add(new Coffee("Tanzanian", true));
        invoice4.add(new Coffee("Algerian", false));
        invoice4.add(new Cake("Coconut Cake", List.of("coconut", "Milk")));
        System.out.println(invoice4);
        // served by John
        // ---
        // coffee Tanzanian with milk
        // coffee Algerian
        // Coconut Cake with coconut, Milk
        // ---
        // price: 6
        invoice4.removeMilkRelatedProducts();
        System.out.println(invoice4);
        // served by John
        // ---
        // coffee Algerian
        // ---
        // price: 1
        
  9. Il arrive qu'un employé ou une star ait besoin de s'absenter ou est fini son service alors qu'une facture a son nom a déjà été créé. Il nous faut une méthode permettant de transférer les cafés d'une facture vers une autres.
    Nous souhaitons pour cela créer une méthode transferFrom qui prend les cafés et les gateaux de la facture pris en paramètre et les copies dans la facture actuelle.
    Faire en sorte que le code suivant fonctionne et affiche les bonnes valeurs.
    Note: comme on va abandonner la facture une fois transférée, il n'est pas nécessaire de supprimer les cafés et gateaux de cette facture.
      public static void main(String[] args) {
        ...
        var invoice5 = new Invoice(john);
        invoice5.add(new Cake("Butter Cake", List.of("butter")));
        var rob = new Employee("Rob");
        var invoice6 = new Invoice(rob);
        invoice6.transferFrom(invoice5);
        System.out.println(invoice6);
        // served by Rob
        // ---
        // Butter Cake with butter
        // ---
        // price: 1
      }
        
  10. Question super-bonus:
    Certains cafés sont très corsés et si une même personne boit tous les cafés, il est possible qu'elle souffre d'un arrêt cardiaque. Pour détecter ce problème et indiquer aux clients, qu'ils ne doivent pas boire tous les cafés qu'ils ont achetés tout seul, on se propose d'ajouter une méthode mayCauseHeartFailure qui prend en paramètre un dictionnaire qui associe le pourcentage de chance d'un arrêt cardiaque (un double) en fonction de l'origine d'un café.
    Dans le cas où un café vient d'un pays qui n'est pas dans le dictionnaire, on considerera que le pourcentage de chance d'un arrêt cardiaque associé est zéro.
    Si pour chaque café, on fait la somme de ces pourcentages et l'on dépasse 1 alors on veut signaler à l'utilisateur qu'il y a un risque de crise cardique.
    Par exemple, avec la facture ci-dessous, il y a 3 café, le premier café engendre un rique de 0.5, le second café un risque de 0.7 et le dernier café un rique de 0 car son origine est pas dans le dictionnaire.
    Note: on peut remarquer que comme la somme pour leux premiers cafés est supérieur à 1, il n'est pas nécessaire de calculer les valeurs des cafés suivants.
    Ecrire la méthode mayCauseHeartFailure et vérifier que code suivant fonctionne et affiche les bonnes valeurs.
      public static void main(String[] args) {
        ...
        var invoice7 = new Invoice(john);
        invoice7.add(new Coffee("Columbian", false));
        invoice7.add(new Cake("Cheese Cake", List.of("milk")));
        invoice7.add(new Coffee("Italian", true));
        invoice7.add(new Coffee("Algerian", false));
        var mayCauseHeartFailure = invoice7.mayCauseHeartFailure(Map.of("Columbian", 0.5, "Italian", 0.7));
        System.out.println(mayCauseHeartFailure);  // true
      }