L'examen est composé de 4 exercices qui doivent être traités dans l'ordre proposé. Il est tout à fait possible de passer un exercice mais il faut avoir lu l'énoncé pour comprendre l'exercice suivant. Il n'est jamais nécessaire d'avoir fait un exercice en entier pour traiter les autres.
Rappel : vous devez configurer le workspace d'Eclipse (File > Switch WorkSpace) pour qu'il corresponde au répertoire EXAM présent dans le home de votre session de TP noté.
Vérifiez que tous vos fichiers sont bien dans le répertoire EXAM. Tout ce qui n'est pas dans ce répertoire est perdu quand vous vous déconnectez (ou en cas de panne).
Vous avez le droit de consulter les transparents du cours.
La Javadoc est disponible ici
Le thème de ce sujet est le manga/animé DragonBall. Aucune connaissance préalable n'est requise bien entendu.
Le sujet est composé comme suit:
D'un point de vue fonctionnel, le code demandé est très simple. Vous allez être évalué sur vos choix dans l'architecture du code: organisation des classes, choix des visibilités pour les champs et les méthodes, absences de duplication de code, respect des principes SOLID, choix des design-patterns...
Dans tout le sujet, sauf mention explicite du contraire, votre code ne doit en aucun cas utiliser le switch sur les classes introduit par Java 17.Toutes les classes demandées dans cet exercice doivent se trouver dans le package fr.uge.dragonball.statistics. Sauf mention explicite du contraire, votre code ne doit en aucun cas utiliser les switch introduits par Java 17.
Dans cet exercice, on cherche à modéliser les statistiques des personnages. Un personnage de base à un nom (name
), un entier donnant sa puissance (power
) et un nombre point de vie maximum (maxHealth
). Un personnage peut aussi être la fusion de plusieurs personnages. Dans ce cas, le personnage a:
Un personnage peut être la fusion d'un nombre arbitraire de personnages de bases et de personnages fusionnés. Par exemple, on peut avoir:
Individual Fighters: Name: Goku, Power: 9000, Max Health: 10000 Name: Vegeta, Power: 8500, Max Health: 9500 Name: Gohan, Power: 7000, Max Health: 3500 Name: Trunk, Power: 6500, Max Health: 4000 Fusion Fighters: Name: <Goku-Vegeta>, Power: 17500, Max Health: 9500 Name: <Gohan-Trunk>, Power: 13500, Max Health: 3500 Mega Fusion: Name: <<Goku-Vegeta>-<Gohan-Trunk>>, Power: 31000, Max Health: 3500
Proposez une modélisation objet de statistiques des personnages. Dans le main d'une classe Application
, construisez les statistiques des personnages donnés dans l'exemple ci-dessus. Si vous avez utilisé des designs patterns, donnez leurs noms en commentaire en haut, de la classe Application
.
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.statistics.q1.
On veut maintenant rajouter le fait que les personnages peuvent se transformer. Une transformation va avoir un nom et un effet sur la puissance et sur les points de vie. Par exemple, la transformation Super Sayen va multiplier la puissance par 2 et diviser les points de vie par deux. La transformation Super Sayen 2 va multiplier la puissance par 4 et diviser les points de vie par deux 4. La transformation Super Blue va multiplier la puissance par les points de vie et mettre les points de vie à 1. Quand un personnage se transforme son nom est obtenu en rajoutant as puis le nom de la transformation comme montré dans les exemples ci-dessous.
Attention, on peut fusionner des personnages transformer et transformer des personnages fusionnés, etc.
With Transformations: Name: Goku as Super Sayen, Power: 18000, Max Health: 5000 Name: <Goku-Vegeta> as Super Sayen Blue, Power: 166250000, Max Health: 1 Name: <Vegeta as Super Sayen Blue-Gohan as Super Sayen 2>, Power: 80778000, Max Health: 1
Adaptez votre modélisation pour faire en sorte de pouvoir gérer des transformations de personnages. Modifier la classe Application
pour créer les exemples donnés ci-dessus. Si vous avez utilisé des designs patterns, donnez leurs noms en commentaire en haut, de la classe Application
.
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.statistics.q2.
On veut interdire que l'utilisateur définisse deux fois une transformation avec des effets différents.
Adaptez votre modélisation pour garantir cette propriété. Modifier la classe Application
en conséquence. Si vous avez utilisé des designs patterns, donnez leurs noms en commentaire en haut, de la classe Application
.
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.statistics.q3.
On veut maintenant qu'un utilisateur utilisant les classes de votre package fr.uge.dragonball.statistics puisse, à l'extérieur de ce package et sans toucher au code des classes de ce package, écrire les fonctions suivantes:
Adaptez votre modélisation pour que ces fonctions soient codables en utilisant les switch sur les classes en dehors du package. Pour convaincre le correcteur que vous savez comment faire, coder la fonction qui extrait les personnages de base dans une fonction static
de la classe Application
.
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.statistics.q4.
Adaptez votre modélisation pour que ces fonctions soient codables sans utiliser les switch sur les classes et en dehors du package. Pour convaincre le correcteur que vous savez comment faire, coder la fonction qui extrait les personnages de base dans une fonction static
de la classe Application
.
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.statistics.q5.
Toutes les classes demandées dans cet exercice doivent se trouver dans le package fr.uge.dragonball.training. Sauf mention explicite du contraire, votre code ne doit en aucun cas utiliser les switches introduits par Java 17.
Dans cet exercice, on modélise une salle d'entraînement magique dans laquelle
on contrôle le temps. La classe TrainingRoom.java représente une salle d'entraînement où des combattants (instances de la classe interne immuable Fighter
) peuvent s'entraîner sur plusieurs jours. Chaque combattant est associé à un compteur de jours, et une fois qu'il atteint un seuil prédéfini (par défaut 10 jours), il est automatiquement retiré de la salle. La méthode newDay()
simule le passage d'une journée, incrémente le compteur pour chaque combattant, et gère leur suppression si nécessaire. La classe garantit une gestion claire et immuable des combattants tout en fournissant une méthode toString()
pour visualiser l'état actuel de la salle d'entraînement.
On veut modifier la classe TrainingRoom
de manière à créer des TrainingRoom
dans lesquelles les combattants sortent au bout de 100 jours, ou bien qu'un combattant sorte quand son nombre de jours dépasse sa puissance divisée par 100.
Modifiez la classe TrainingRoom
pour obtenir le comportement demandé. Dans une classe Application
, créez les deux TrainingRoom
mentionnées précédemment. Si vous avez utilisé des design patterns, donnez leurs noms en commentaire en haut de la classe TrainingRoom
.
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.training.q1.
À la fin d'un jour, on veut envoyer un mail à arnaud.carayol@univ-eiffel.fr avec le message "It is over 9000" si la puissance cumulée des combattants dépasse 9000. Au départ d'un combattant, on veut faire un post sur Instagram avec la liste ordonnée de tous les combattants qui sont partis avant lui. Quand un combattant arrive et que sa puissance est supérieure à celle de tous les autres combattants, on veut faire une annonce sur Facebook. Pour simplifier les choses, un simple affichage suffit pour l'envoi de mails et les posts sur Instagram et Facebook.
Modifiez la classe TrainingRoom
pour obtenir le comportement demandé. Dans une classe Application
, créez une TrainingRoom
avec le comportement demandé. Si vous avez utilisé des design patterns, donnez leurs noms en commentaire en haut de la classe TrainingRoom
.
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.training.q2.
Toutes les classes demandées dans cet exercice doivent se trouver dans le package fr.uge.dragonball.locator. Sauf mention explicite du contraire, votre code ne doit en aucun cas utiliser les switch introduits par Java 17.
Les API fournies dans cet exercice sont volontairment mal conçues et ne doivent en aucun cas servir de source d'inspiration pour un quelconque code !
Les boules de cristal (Dragon Ball) sont au nombre de 7, numérotées de 1 à 7. Elles sont cachées sur Terre et pour les trouver, nous disposons d'une API téléchargeable BulmaLocator.java.
L'API BulmaLocator
ne contient qu'une seule méthode publique qui renvoie une liste de taille 7 qui contient les positions des dragonballs dans l'ordre avec null
si la Dragon Ball correspondante n'a pas pu être localisée. La position d'une Dragon Ball est une List<Integer> qui contient en première coordonnée la longitude et en seconde coordonnées la latitude.
L'API est mal conçue sur plusieurs aspects. Un aspect particulièrement problématique est que l'API calcule la position de toutes les Dragon Ball même si l'utilisateur final n'en utilise qu'une seule. C'est dommage car il y avait une méthode privée qui localise une seule Dragon Ball. Mais ce qui est fait est fait ...
Pour cette question et cette question seulement, on va se poser la question d'améliorer l'API BulmaLocator
. La contrainte est que le code déjà
écrit qui utilise BulmaLocator
doit continuer à fonctionner sans modification.
On voudrait modifier le code de BulmaLocator
pour que le code suivant ne localise que la dragonball numéro 1.
var locator = new BulmaLocator(); var position = locator.locateAll().get(0);
Copiez l'API BulmaLocator
dans le package fr.uge.dragonball.training et modifiez la pour obtenir le comportement demandé. Si vous avez utilisé des designs patterns, donnez leurs noms en commentaire en haut, de la classe BulmaLocator
.
Il existe en Java une classe AbstractList
. Intéressant, non ?
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.locator.q1.
A partir de maintenant, vous devez utiliser l'API BulmaLocator
donnée initialement et il est interdit de modifier les API données.
Vous travaillez sur une petite application écrite par un stagiaire Chad Gépithé. Le code est disponible DragonBallFinder.java
On vous demande de modifier l'application pour utiliser une nouvelle API de localisation RedArmyLocator.java. Si l'application est lancée avec l'option -rm, on veut utiliser cette nouvelle API et sinon l'ancienne.
L'API de localisation RedArmyLocator n'utilise pas le même système de coordonnées que l'API BulmaLocator. En lisant la javadoc, vous trouverez la formule de conversion à appliquer. Vous verrez également qu'il existe une méthode rapide permettant de localiser une Dragon Ball en particulier ainsi qu'une méthode pour localiser toutes les Dragon Ball. Il serait intéressant de pouvoir utiliser ces deux méthodes, réunir les 7 Dragon Ball n'étant déjà pas une tâche facile.
Modifiez l'application DragonBallFinder
pour obtenir le comportement demandé. Si vous avez utilisé des designs patterns, donnez leurs noms en commentaire en haut, de la classe DragonBallFinder
.
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.locator.q2.
On veut maintenant que si l'application est lancée avec l'option -mix, elle essaie de localiser d'abord avec BulmaLocator
puis avec RedArmyLocator
pour les Dragon Ball non localisées. Avec l'option -mix-reversed, on veut le même comportement mais avec RedArmyLocator
puis BulmaLocator
.
Modifiez l'application DragonBallFinder
pour obtenir le comportement demandé. Si vous avez utilisé des designs patterns, donnez leurs noms en commentaire en haut, de la classe DragonBallFinder
. Attention à la duplication de code.
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.locator.q3.
Toutes les classes demandées dans cet exercice doivent se trouver dans le package fr.uge.dragonball.timeseries. Sauf mention explicite du contraire, votre code ne doit en aucun cas utiliser les switch introduits par Java 17.
La classe TimeSeries.java représente une série temporelle de données associant des instants temporels (Instant
) à des niveaux de puissance (Long
). Elle offre des fonctionnalités essentielles pour interroger ces données de manière efficace.
Les méthodes principales sont:
String name()
renvoie le nom de la série temporelleOptionalLong valueAt(Instant instant)
renvoie la valeur de la série temporelle enregistrée au moment le plus proche de l'instant fourni.Set<Instant> instants()
permet de récupérer l'ensemble des instants disponibles dans la série.Les TimeSeries
sont créées à l'aide de la méthode static fromWeb(String name, Instant start, int nbPoints)
: qui génère une série temporelle en simulant une collecte de données via une API fictive PowerLevelReporter.java, en créant un nombre défini de points avec un écart d'une minute entre chaque échantillon à partir de l'instant start
.
Téléchargez les classes PowerLevelReporter.java, TimeSeries.java et Application.java qui donnent un exemple simple d'utilisation.
On vous demande de travailler sur la classe TimeSeries
pour la faire évoluer.
Rajouter une manière user-friendly de construire une TimeSeries
à la main. Le constructeur doit rester privé et la classe TimeSeries
doit rester immutable. Si vous avez utilisé des designs patterns, donnez leurs noms en commentaire en haut, de la classe TimeSeries
.
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.timeseries.q1.
On oublie le code de la question précédente et l'on repart du code de TimeSeries
donné initialement. On suppose que la classe TimeSeries
est utilisée par des millions d'utilisateurs de par le monde. Il est donc hors de question d'introduire des changements qui vont casser le code existant. On voudrait que si l'on appelle fromWeb
avec plus de 10 points, la TimeSerie
résultante a bien le même ensemble instants
mais les valeurs ne sont récupérées par l'API PowerLevelReporter
que quand elles sont utilisées. S'il y a moins de 10 points, on veut conserver le même comportement.
Modifiez le code pour obtenir le comportement voulu sans casser le code existant qui utilise TimeSeries
. Si vous avez utilisé des designs patterns, donnez leurs noms en commentaire en haut, de la classe TimeSeries
.
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.timeseries.q2.
On voudrait maintenant pouvoir créer à partir d'une TimeSeries
une TimeSeries
qui, à un instant donné, contient la somme de toutes les valeurs avant cet instant (inclus). On ne veut pas dupliquer les données de la TimeSeries
originale (i.e. les valeurs seront recalculées à chaque fois).
Modifiez le code pour obtenir le comportement. Si vous avez utilisé des designs patterns, donnez leurs noms en commentaire en haut, de la classe TimeSeries
.
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.timeseries.q3.
On voudrait maintenant pouvoir créer à partir d'une TimeSeries
une TimeSeries
qui, à un instant donné, contient la moyenne de toutes les valeurs avant cet instant (inclus). Là encore, on ne veut pas dupliquer les données de la TimeSeries
originale (i.e. les valeurs seront recalculées à chaque fois). On voudrait aussi pouvoir créer une TimeSeries
qui, à un instant donné, contient la moyenne des 10 derniers instants avant cet instant (inclus).
Modifiez le code pour obtenir le comportement voulu en minimisant la duplication de code et en facilitant la création de TimeSeries
du même genre. Si vous avez utilisé des designs patterns, donnez leurs noms en commentaire en haut, de la classe TimeSeries
.
Quand vous avez fini de faire cette question, copiez toutes les classes dans le sous-package fr.uge.dragonball.timeseries.q4.