:: Enseignements :: ESIPE :: E3INFO :: 2013-2014 :: Programmation Objet avec Java ::
[LOGO]

TP noté - programmation orientée objet avec Java


Le but de ce TP noté est d'écrire quelques classes simples 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 héritage ou implémentation d'interfaces, ainsi que l'utilisation de collections élémentaires pour la réalisation de petits algorithmes simples en Java.

Vos sources Java produites pendant le TP devront être placées sous le répertoire EXAM à la racine de votre compte; sinon, elles ne seront pas récupérées.

Vous pouvez consulter la javadoc à travers eclipse ou en lançant la commande jdoc dans un shell. Les seuls documents papiers autorisés sont les supports de cours photocopiés qui vous ont été distribués et les notes de cours ou de TD.

On souhaite écrire des classes modélisant des articles (en vente dans un super-marché par exemple), ainsi qu'un panier d'articles permettant de les stocker.

Exercice 1 - Des articles (3 points)

  1. Écrire une classe Item représentant un article ayant deux champs, name pour son nom de type String et price pour son prix en centimes de type long (on supposera que toutes les manipulations de prix doivent se faire avec des entiers long).
    Cette classe devra posséder deux accesseurs permettant d'obtenir le nom de l'article et son prix, ainsi qu'un constructeur qui permet d'en créer des instances.
    On veut que le prix d'un article et son nom ne changent pas pour toute la durée de vie de l'objet.
    Écrire une classe Main permettant de tester la création d'un article et le fonctionnement des accesseurs de la manière suivante:
      Item item = new Item("corn flakes", 500);
      System.out.println(item.getPrice());        // affiche: 500
      System.out.println(item.getName());         // affiche: corn flakes
    
  2. On souhaite que lorsque l'on affiche un article avec System.out.println(), le nom de l'article s'affiche suivi de deux points (":"), d'un espace et de son prix en euros, où les centimes sont précédés d'un point ('.'), le tout suivi d'un espace et du symbole '€'. Par exemple:
      Item item = new Item("corn flakes", 500)
      System.out.println(item);                   // affiche: corn flakes: 5.00 €
      Item chewingGum = new Item("chewing gum",403);
      System.out.println(chewingGum);             // affiche: chewing gum: 4.03 €
    
    Indication: utilisez la méthode String.format() qui fonctionne comme printf(): String.format("%d.%02d", 100, 1) produit la chaîne "100.01".

Exercice 2 - Le panier d'achats (8 points)

  1. Écrire une classe ShoppingCart modélisant un panier d'articles dans lequel on peut:
    • ajouter un article avec addItem();
    • retirer un article avec removeItem(), qui devra renvoyer false si l'item que l'on essaye de supprimer n'existe pas;
    • connaître le nombre d'articles avec itemCount();
    • calculer le prix total du panier avec totalPrice() (vous écrirez en commentaire au début de la méthode quel est l'ordre de grandeur de la complexité de cette méthode);
    Indication: utilisez une collection de java.util pour stocker les articles du panier.
    Modifier votre Main pour faire des tests en utilisant le code ci-dessous
      Item item1 = new Item("corn flakes", 500);
      Item item2 = new Item("caviar", 50000);
      Item item3 = new Item("water", 101);
      ShoppingCart cart = new ShoppingCart();
      cart.addItem(item1);
      cart.addItem(item2);
      cart.addItem(item3);
      System.out.println(cart.itemCount());       // affiche: 3
      System.out.println(cart.totalPrice());      // affiche: 50601
    
  2. Vérifier que le code suivant affiche bien 0.
      ShoppingCart cart = new ShoppingCart();
      Item item = new Item("corn flakes", 500);
      cart.addItem(item);
      cart.removeItem(new Item("corn flakes", 500));
      System.out.println(cart.itemCount());       // affiche: 0
    
    Sinon, changer votre implémentation en conséquence.
  3. Ajouter un poids (weight de type int) exprimé en grammes à la classe Item et modifiez le constructeur pour qu'il accepte ce paramètre.
    Modifier l'implantation de votre panier pour que l'on ne puisse pas ajouter d'article dans le panier si le poids de ce dernier doit dépasser 10 kg. Vous utiliserez pour cela l'exception IllegalStateException.
    Attention: le test du poids devra se faire en temps constant (O(1)). Modifier votre Main pour vérifer que cela fonctionne. Par exemple, testez:
      Item item1 = new Item("corn flakes", 501, 1000);
      Item item2 = new Item("caviar", 50000, 500);
      Item item3 = new Item("water", 500, 5000);
      ShoppingCart cart = new ShoppingCart();
      cart.addItem(item1);
      cart.addItem(item2);
      cart.addItem(item3);      
      // cart.addItem(item3);     // lève java.lang.IllegalStateException
      cart.removeItem(new Item("eau", 500, 5000));
      // cart.addItem(item3);     // lève java.lang.IllegalStateException
      cart.removeItem(new Item("water", 500, 5000));
      cart.addItem(item3);        // ajout possible!
    
    Indication: penser que removeItem() a une valeur de retour.
  4. On souhaite que chaque panier d'achat créé puisse disposer automatiquement à sa création d'un numéro de série unique (qui commence à 1 et qui est incrémenté de 1 à chaque nouveau panier créé), et qui soit connu comme l'identifiant (id) de ce panier.
    Ajoutez une méthode getId() à la classe ShoppingCart qui retourne cet entier de type int, et tout ce dont vous avez besoin pour l'implémenter. Par exemple, vous pouvez tester avec le code suivant.
      ShoppingCart c1 = new ShoppingCart();
      System.out.println(c1.getId());         // affiche: 1
      Item item1 = new Item("corn flakes", 501, 1000);
      c1.addItem(item1);
      Item item2 = new Item("caviar", 50000, 500);
      c1.addItem(item2);
      System.out.println(c1.getId());         // affiche: 1
      ShoppingCart c2 = new ShoppingCart();
      ShoppingCart c3 = new ShoppingCart();
      Item item3 = new Item("water", 500, 5000);
      c3.addItem(item3);
      System.out.println(c2.getId());         // affiche: 2
      System.out.println(c3.getId());         // affiche: 3
    
  5. Ajouter à la classe ShoppingCart une méthode toString() qui retourne une représentation du contenu du panier, commençant par l'identifiant unique du panier et le nombre d'articles contenus, puis affichant tous les articles du panier, un article par ligne. Par exemple, avec le code d'exemple de la question précédente, vous devez obtenir:
      System.out.println(c1); // affiche: panier 1 [2 article(s)]
                              //            corn flakes: 5.01 €
                              //            caviar: 500.00 €
                              
      System.out.println(c2); // affiche: panier 2 [0 article(s)]
      
      System.out.println(c3); // affiche: panier 3 [1 article(s)]
                              //            water: 5.00 €
    

Exercice 3 - Des produits frais (3 points)

Certains articles sont particulièrement frais et peuvent nécessiter la prise en compte d'une date limite de consommation. Néanmoins, lorsqu'on les met dans un panier d'achat, ils se comportent comme des articles classiques et ont un nom, un prix et un poids.

  1. Écrire une classe (FreshItem) qui correspond à un article frais ayant, en plus des informations stockées dans la classe Item, un champ bestBeforeDate de type String correspondant à la date limite de consommation au format YYYY-MM-DD.
    L'affichage d'un article frais par System.out.println() doit afficher les informations de l'article dans le même format que pour un Item, mais précédées de la date limite de consommation. Testez avec:
      Item item1 = new Item("corn flakes", 500, 1000);
      System.out.println(item1);     // affiche: corn flakes: 5.00€
      FreshItem fresh = new FreshItem("Salmon", 1450, 800, "2012-04-11");
      System.out.println(fresh);     // affiche: BBD:2012-04-11 Salmon: 14.50€
    
  2. Verifier que le code suivant fonctionne, sinon faites les changements qui s'imposent.
      Item tin = new Item("sardine", 500, 500);
      FreshItem fresh = new FreshItem("sardine", 500, 500, "2012-04-11");
      ShoppingCart cart = new ShoppingCart();
      cart.addItem(fresh);
      cart.removeItem(tin);
      System.out.println(cart); // affiche: panier 1 [1 article(s)]
                                //            BBD: 2012-04-11 sardine: 5.00 €
    

Exercice 4 - La petite note (6 points)

On souhaite maintenant éditer des factures, mais pas seulement d'articles ou d'articles frais, mais plus généralement pour une liste de choses qui peuvent être payées.

  1. Pour commencer, on représente par le type Ticket des billets d'évènements ou de spectacles qui peuvent être vendus dans le même magasin que nos articles (Item) ou nos articles frais (FreshItem).
    Un billet est représenté par une référence (reference de type String) et par un prix en centimes d'euros (price de type long).
    Écrire une classe Ticket avec les champs nécessaires et un constructeur permettant de créer, par exemple:
      Ticket ticket = new Ticket("RGBY17032012 - Walles-France", 9000);
    
  2. On souhaite maintenant disposer d'un type Payable, qui dispose des méthodes:
    • label() qui retourne une String représentant une descritpion textuelle de ce qui doit être payé;
    • cost() qui retourne le coût de ce qui doit être payé en centimes (un entier long);
    • taxRatePerTenThousand() qui retourne la proportion du coût qui est de la taxe, exprimée en centièmes de pourcents (en "pour-dix-mille") sous la forme d'un entier long. Par exemple, dans cette unité, 550 représente un taux de taxe de 5,5% et 1960 représente un taux de taxe de 19,6%;
    Définir le type Payable et modifier la classe Ticket de sorte que le code suivant fonctionne (on considérera que toutes les instances de la classe Ticket sont par défaut taxées à 25%):
      Payable payable = new Ticket("RGBY17032012 - Walles-France", 9000);
      System.out.println(payable.label());                  // affiche: RGBY17032012 - Walles-France
      System.out.println(payable.cost());                   // affiche: 9000
      System.out.println(payable.taxRatePerTenThousand());  // affiche: 2500
    
  3. On représente maintenant une facture comme une liste de choses à payer. Créer une classe Invoice qui dispose pour l'instant d'un seul constructeur sans argument et d'une unique méthode add(Payable p) qui ajoute à la liste de choses à payer l'argument p. Le code suivant doit fonctionner:
      Invoice invoice = new Invoice();
      Payable payable = new Ticket("RGBY20120317 - Walles-France", 9000);
      Ticket ticket = new Ticket("MUSI20120612 - RollingStones",12000);
      invoice.add(payable);
      invoice.add(ticket);
    
  4. Faites ce qu'il faut pour que la dernière ligne du code suivant fonctionne également (toutes les instances de la classe Item sont taxées à 10% par défaut).
      Invoice invoice = new Invoice();
      Payable payable = new Ticket("RGBY20120317 - Walles-France", 9000);
      Ticket ticket = new Ticket("MUSI20120612 - RollingStones",12000);
      invoice.add(payable);
      invoice.add(ticket);
      Item item = new Item("corn flakes", 500, 1000);
      invoice.add(item);
    
  5. Pour les articles frais, instances de FreshItem, la taxe est réduite de 0,1% par tranche d'un kilo de produit. Par exemple, les sardines en boite sont taxées à 10%, mais les sardines fraiches sont taxées à 10% s'il y en a moins d'1 kg, et à 10% - 0,1% s'il y en a entre 1 et 2 kg... Modifiez ce qu'il faut et vérifiez:
      Item tin = new Item("sardine", 500, 500);
      FreshItem fresh = new FreshItem("sardine", 500, 500, "2012-04-11");
      FreshItem fresh2 = new FreshItem("sardine x3", 1500, 1500, "2012-04-11");
      System.out.println(tin.taxRatePerTenThousand());     // affiche: 1000 
      System.out.println(fresh.taxRatePerTenThousand());   // affiche: 1000
      System.out.println(fresh2.taxRatePerTenThousand());  // affiche:  990
    
  6. On souhaite ajouter dans la classe Invoice deux méthodes: totalAmount() qui retourne un long représentant le montant total des choses à payer de cette facture, et totalTax() qui retourne un long représentant le montant total des taxes de cette facture. Que proposez vous pour que le code suivant ne soit pas obligé de parcourir 2 fois l'ensemble des articles de la facture.
      Invoice invoice = new Invoice();
      invoice.add(tin);
      invoice.add(fresh);
      invoice.add(fresh2);
      System.out.println(invoice.totalAmount());  // affiche: 2500
      System.out.println(invoice.totalTax());     // affiche:  248