Teaching - 2i002 - (TME: sujets)


2i002 : Introduction à la programmation Objet

On considère une machine à café. A la base du système, nous allons d'abord considérer les ingrédients : le café , l'eau , le lait , le chocolat . La machine à café est ensuite constituée de réservoirs pour les ingrédients et de recettes (expresso, café allongé, café au lait, chocolat chaud...). Afin d'éviter de recharger la machine en eau trop souvent, elle sera reliée à un robinet qui sera modélisé comme un réservoir de capacité infinie.

Exercice 1 : Eléments de base

Question 1 :

Hiérarchie(s) de classes. Situer tous les éléments en gras de la description dans une arborescence de classes en utilisant les liens d'héritage et de composition. Indiquer la/les classe(s) abstraite(s).

Question 2 :

Donner le code de la classe abstraite Ingredient contenant un attribut String nom , un constructeur à un argument pour initialiser ce nom, un méthode toString.
Dans la suite de l'exercice, le nom servira à décrire l'ingrédient (e.g. "café" , "chocolat" ...)

Question 3 :

Ajouter une méthode standard equals testant l'égalité structurelle entre 2 ingrédients (c'est à dire entre leurs noms ) en traitant le cas où nom n'est pas instancié (null ).
Note: attention à bien considérer la classe String comme un objet et pas un type de base.

Question 4 :

Une classe abstraite a-t-elle forcément une méthode abstraite?

Question 5 :

Donner le code de la classe Cafe héritant d'Ingredient . Le constructeur ne prend pas d'argument: le nom étant toujours cafe .

On imagine dans la suite du problème que tous les ingrédients (classes Eau , Lait et Chocolat ) ont également été créés avec un constructeur sans paramètre.

Exercice 2 : Reservoir

Question 1 :

Donner le code d'une classe RecuperationIngredientException qui étend les exceptions et qui a un constructeur à un argument (String message ).

Question 2 :

Le réservoir a pour attribut un Ingredient , une capacite (un réel exprimé en litre), un niveau (également réel exprimé en litre). A la construction le réservoir est plein. Le réservoir dispose d'un accesseur sur l'ingrédient et d'une méthode remplir qui le remplit totalement.
Donner le code de la classe.

Question 3 :

Le réservoir a aussi une méthode recuperer qui prend en argument un réel (la quantité souhaitée par le client): la méthode teste si le niveau est suffisant et, dans l'affirmative, décrémente le niveau , sinon elle lève une RecuperationIngredientException avec un message expliquant le problème (type d'ingrédient et quantité disponible). Une fois sur mille (aléatoirement), le réservoir connait une défaillance et n'est pas capable de délivrer l'ingrédient: vous lèverez une exception dans ce cas, également avec un message explicite.
Donner le code de la méthode en portant une attention particulière à la signature.

Question 4 :

Donner le code de la classe Robinet , qui est un réservoir de capacité infinie (Double.POSITIVE_INFINITY ) qui rencontre un problème quand on s'en sert une fois sur 500 (aléatoirement). Le message à donner à l'utilisateur est de vérifier que le robinet est bien ouvert.

Exercice 3 : Recette

Une recette est composée d'un tableau d'ingrédients de taille fixe et d'un tableau de réels indiquant les quantités (toujours en litre). Elle a aussi un prix (réel, en euros) et un nom. Le constructeur prend en arguments tous les éléments nécessaires à l'initialisation des attributs. La classe possède des accesseurs sur tous ses champs.

Question 1 :

Donner le code de la classe: attributs + constructeur (aucun piège ici, donner rapidement le code). On considère que les accesseurs existent et s'appellent getNomAttribut() (pas la peine de donner le code pour gagner du temps).

Exercice 4 : Machine

La machine est composée de deux listes dynamiques (ArrayList \footnote{Mini-documentation disponible avant le problème.}) de recettes et de reservoirs. La machine gère un crédit (réel, en euros) et elle a un identifiant entier unique que vous initialiserez avec un compteur static.

De manière générale, la machine fonctionne de la manière suivante. D'abord, l'utilisateur ajoute de l'argent à la machine, puis sélectionne une recette, enfin la machine prépare la mixture en récupérant chaque ingrédient dans son réservoir. La machine sait rendre la monnaie (dans la pratique, on mettra simplement le crédit disponible à 0).

Question 1 :

Donner le code de base de la classe Machine avec un constructeur sans argument et deux méthodes public void ajouterReservoir(Reservoir r) et public void ajouterRecette(Recette r) pour configurer la machine. La classe possède aussi une méthode public void ajouterCredit(double d) pour mettre de l'argent (en euros) et une méthode public void rendreLaMonnaie() qui met le crédit à 0.

Question 2 :

Donner le code de la méthode public void remplir() qui correspond à l'action de l'agent d'entretien consistant à remplir tous les réservoirs au maximum.

Question 3 :

Nous allons maintenant procéder au check-up de la machine pour vérifier que tout est OK. Nous avons besoin d'une méthode private Reservoir trouverReservoir(Ingredient i) qui retourne le réservoir associé à l'ingrédient i s'il existe dans la machine et null sinon. On suppose qu'il n'y a qu'un réservoir par ingrédient pour éviter les complications.
Donner le code de cette méthode et expliquer en une phrase pourquoi nous l'avons déclaré private .

Question 4 :

Donner le code de la méthode public boolean checkup() qui vérifie si toutes les recettes sont réalisables, c'est à dire, si tous les ingrédients de toutes les recettes sont bien disponibles dans la machine (à la première erreur, le test est interrompu et retourne false ). Cette méthode affiche dans la console le nom des recettes avec la mention OK à coté si la recette est réalisable. Elle retourne true si toutes les recettes sont OK.

Question 5 :

Donner le code de la méthode public boolean commander(int ri) correspondant à l'appui sur le bouton ri de la machine. La machine indique la recette sélectionnée (si elle existe), vérifie que l'utilisateur a assez de crédit et prépare la recette en affichant des messages au fur et à mesure de la préparation. En cas de problème lors de la préparation, la machine affiche un message et retourne false . Le client n'est débité que si tout s'est bien passé (dans ce cas, on retourne true ).
Note: on considère ici que le check-up a été passé avec succès, tous les réservoirs associés à une recette sont toujours disponibles (ils peuvent simplement être vides ou en panne).

Exercice 5 : Test

Question 1 :

Donner le code du programme TestMachineACafe permettant de valider le bon fonctionnement de la machine : création d'une recette simple à 2 ingrédients (et ajout dans la machine), création des 2 réservoirs associés (et ajout dans la machine), check-up, test sur la monnaie disponible, test pour distribuer des boissons jusqu'à provoquer une erreur.

Exercice 6 : Extensions bonus

Question 1 :

Pour créer une machine connectée, on invente un nouveau type de ReservoirConnecte qui est construit avec 3 arguments (Ingredient ingredient, double capacite, String adresse) . Il a une méthode void mailTo(String message) permettant d'envoyer un mail à la compagnie qui gère la machine (en utilisant l'adresse donnée lors de la construction). La machine envoie automatiquement un mail lorsqu'un réservoir passe à un niveau de remplissage inférieur à 10% (dans la pratique, on affiche simplement un message dans la console).
Donner le code de cette classe.

Note : on supposera qu'il existe des accesseurs pour les attributs niveau et capacite dans la classe Reservoir .

Question 2 :

L'architecture de la machine est-elle satisfaisante et évolutive? Justifier en expliquant la démarche pour ajouter un programme soupe à la tomate.