TP 7-8 — Collections
Choisir la bonne structure de données : annuaire bidirectionnel à deux HashMap, élevage de volailles en Collection, mise en rayon avec Comparable et viviers de participants en HashSet.
Objectif des TP : choisir et manipuler les bonnes collections — deux HashMap en
parallèle pour un annuaire bidirectionnel, une Collection pour un élevage hétérogène,
un HashSet quand l'unicité est une règle métier — et câbler equals/hashCode là où
les structures en ont besoin.
TP 7 — Annuaire bidirectionnel
Le contexte restreint un recyclage : la classe PhoneNumber, fournie en jar (elle
provenait d'un TD, paquetage tdOO.td01), est trop générique — pays 33, zone 03 et
secteur 20 sont fixes pour l'université ; restent le code de composante (1 chiffre)
et le numéro de poste (4 chiffres). En interne on ne compose que les 5 derniers
chiffres ; en externe, tous.
- L'enum
UniversityDepartment(squelette fourni) porte les 8 composantes avec leurs indicatifs de 1 à 8 ; à compléter avecgetDiallingCode(),getLabelShort()(le sigle) etgetLabelLong(). ProPhoneNumberencapsule laPhoneNumberfournie : constructeur(String fourDigits, UniversityDepartment dept), puis les deux affichages imposés —internToString():31234(IUT);externToString():+33.3.20.03.12.34 (Institut Universitaire de Technologies). Unequals(Object)classique plus une surchargeequals(String fourDigits)sur le seul poste. La classe de testsProPhoneNumberTestest fournie.BidirectionalPhoneBookrepose sur deux tables gérées en parallèle :name2num : Map<String, ProPhoneNumber>etnum2name : Map<String, String>(clé = les 5 chiffres internes). Chaque opération maintient les deux tables :addrenvoiefalsesialreadyRegisteredtrouve la chaîne — nom ou 5 chiffres — dans l'une ou l'autre. Les recherches deviennent triviales dans les deux sens (getProPhoneNumberFromName,getNameFromFiveDigits), etlisting()groupe les numéros par composante, nom long en tête, format interne sur chaque ligne.
La classe de tests BidirectionalPhoneBookTest est fournie.
TP 7 — Élevage de volailles
Poultry est abstraite : bague (identity) et weight sont communs et
protected ; prix au kilo et seuil d'abattage sont propres à chaque espèce mais
partagés par tous ses individus — ce sont donc des variables de classe
(priceKg, slaughterThreshold) définies dans Duck (1,5 €/kg, 5 kg), Hen
(1 €/kg, 3,5 kg) et Goose (4 €/kg, 10 kg), modifiables via setPriceKg et
setSlaughterThreshold.
La décomposition du code fait l'exercice : la formule du prix (poids × €/kg) est
identique pour toutes les espèces — getPrice() s'écrit une fois dans la mère, en
invoquant des accesseurs aux variables de classe ; l'affichage Hen [1,2.3] passe par un
toString lui aussi factorisé. Les classes de tests DuckTest, HenTest,
GooseTest sont fournies.
BreedingFarm stocke ses bêtes dans une seule Collection<Poultry> (taille
maximale 200 par défaut) : add si la limite n'est pas atteinte, getFarmSize,
getNbDuck, search(int identity), getDuck(int i), updateWeight. Les questions
financières imposent de filtrer par aptitude : potentialProfit() somme les bêtes dont
le poids dépasse le seuil d'abattage, slaughtering() les retire de l'élevage et
renvoie la List<Poultry> partie à l'abattoir. Le foie gras arrive par une interface
IForceFeeding (oies et canards seulement) qui sert à calculer
potentialLuxuryProfit() — le gain doublé sur les volailles concernées. La classe de
tests BreedingFarmTest est fournie.
TP 8 — Mise en rayon : interface et Comparable
L'organisation est donnée par l'UML : l'interface IProduct (getPrice,
isPerishable) unifie Food et Furniture pour que la mise en rayon soit un
traitement unique — « sans faire de test pour déterminer son type réel », dit
l'énoncé.
Food: labelnullremplacé parrefUnknownXXX(compteur de références inconnues), date limite par défaut à 10 jours de la date courante,isBestBefore(LocalDate), et l'affichage[pasta=3.25 -> before 2019-01-01].- Le tri par date limite se fait en rendant
FoodComparable: le programmeUseComparablede l'énoncé doit réordonner le stock après un simpleCollections.sort(storage)— les plus proches de péremption d'abord. Shelf: réfrigérée ou non, capacité maximale,ArrayList<IProduct>;add(IProduct)vérifie compatibilité et place restante ; l'affichage attendu est[false : 1 -> []](non réfrigérée, capacité 1, vide).Shop.tidy(ArrayList<IProduct>)répartit le stock sur les étagères ;closeBestBeforeDate(int nbDays)liste les périssables dont l'échéance approche. La classe de testsShopTestest fournie.
TP 8 — Concours : HashSet et association réflexive
Participant porte une association réflexive partner (0..1) : matchWith doit
câbler les deux côtés et renvoyer false si l'un des deux a déjà un partenaire ;
breakOff délie les deux côtés aussi — « attention à la réflexivité ! », répète
l'énoncé. hashCode et equals sont générés par l'IDE sur les bons attributs :
c'est le préalable indispensable, car le vivier BreedingGround repose sur un
HashSet et chaque participant ne peut s'inscrire qu'une seule fois
(registration refuse les doublons).
Le reste des méthodes exploite cette unicité : loners() (participants sans partenaire),
lonersCleansing(), forcedMatching() (appariement aléatoire des isolés), puis la
chasse aux tricheurs entre deux viviers — cheaters (inscrits dans les deux),
isolateCheaters (délier avant d'exclure !), cheatersCleansing — et les fusions :
possibleMerging, fusion, securedMerging (bannir les tricheurs puis fusionner). La
classe de tests BreedingGroundTest est fournie.
Le choix de la collection, résumé
Deux tables en parallèle pour associer dans les deux sens ; une Collection polymorphe
pour un élevage hétérogène ; un HashSet quand l'unicité fait partie des règles métier —
avec, à chaque fois, le couple equals/hashCode correct.
Parce que l'annuaire est bidirectionnel : trouver le numéro depuis le nom et le nom depuis le numéro doivent être des accès directs. Avec une seule table, le sens inverse exigerait un parcours complet — la seconde table paie la recherche en O(1) au prix d'une mise à jour double.
Dans la classe abstraite Poultry : la formule est identique pour toutes les
espèces, seules les valeurs du prix au kilo changent — et elles sont accessibles par
les accesseurs aux variables de classe. Dupliquer getPrice() dans chaque fille
serait une redondance inutile.
Parce que le HashSet s'appuie sur les deux : le hash choisit la case, equals
départage les collisions. Deux participants égaux doivent avoir le même hashcode,
sinon l'unicité promise par l'ensemble tombe — et les tricheurs passeraient à
travers les mailles.
Parce que son partenaire resterait associé à quelqu'un qui quitte le vivier :
isolateCheaters commence par un breakOff — l'association réflexive impose de
nettoyer les deux côtés avant toute suppression.
tpOO_07.pdf
R2.01 · TP 7 — Tables associatives / élevage de volailles · 5 pages
Chargement du document…
tpOO_08.pdf
R2.01 · TP 8 — Mise en rayon / concours · 5 pages
Chargement du document…
Dernière mise à jour le
TP 5-6 — Héritage et hiérarchies
Construire ses premières hiérarchies de classes avec des tests JUnit fournis : bibliothèque et articles périssables, puis salaires d'une entreprise et revente de véhicules de collection.
TP 9-10 — Exceptions et génériques
Exceptions personnalisées, fichiers récursifs et Scanner — puis l'ordonnanceur de tâches générique en quatre phases, de la file simple à l'équité : la synthèse finale du semestre.