====== Design Patterns en action ======
Voici quelques exercices simples mettant en jeu quelques DPs.
Nous vous demandons de modéliser.
**Cette modélisation peut être réalisée sur papier**. L'essentiel est d'avoir toujours le modèle de votre application en tête.
Les temps donnés sont là pour vous aider à vous évaluer. Cependant, si vous avez besoin d'un peu plus de temps pour bien faire les exercices, alors prenez-le parce que le véritable objectif est bien que vous sachiez faire seul !
===== Partie 1 : Les briques de base (20 min) =====
Commencer par modéliser sur papier, le problème sous la forme d'un diagramme de classe.
On souhaite modéliser le fonctionnement d’un jeu comportant différents types de personnages : des humains (nom + couleur des yeux), des trolls (nom + nom bière préférée), des orcs(nom + taille des oreilles) et des Taurens (nom + taille de la corne)...
Vous avez ici un modèle de classes... Vous pourrez n'implémenter que deux types de personnages. C'est suffisant pour tester la suite.
Au début de sa vie un personnage a une vitalité de 100.
Il est affaibli au cours de sa vie, ce qui réduit sa vitalité.
Les personnages savent se battre en utilisant des armes qui peuvent être des dagues, des épées et des boucliers.
Un personnage ne manipule qu’une seule arme à la fois, au moins dans un premier temps. Cependant, il peut changer d’arme plusieurs fois au cours du déroulement du jeu.
Une arme a une force d'attaque et une force protectrice ((Elle a d'autres propriétés que nous ne discutons pas ici, mais une épée n'est pas manipuler de la même manière qu'un bouclier, etc. Donc en fonction du fait qu'il s'agit d'une épée ou d'un bouclier, on aura des comportements différents, mais c'est une autre "histoire".))
Pour l'instant on considère ces forces comme des constantes :
* dague : force : 20, protection : 10
* epee : force : 25, protection : 5
* bouclier : force : 5, protection : 20
Vous avez ici un modèle de classes complet ... Vous pourrez n'implémenter que deux classes d'armes. C'est suffisant pour tester la suite.
L’utilisateur du jeu doit avoir la possibilité d’ordonner à un personnage (A) de se battre contre un autre (B). Celui qui est attaqué (B) voit sa vitalité décroitre d'un coefficient qui correspond à la //force de l'attaquant (force de son arme A) - sa propre force protectrice B (force protectrice de son arme B)//.
A chaque personnage est associé à une arme par défaut (Orcs : épée ; Troll : dague ; Tauren : bouclier, Humain : épée).
Modélisez, codez et testez vos classes y compris avec le scénario suivant :
- Créer un personnage nommé « Diablon » de type « Tauren » avec une corne de 15 cm;
- Créer un personnage nommé « Azag» de type « Orc » avec des oreilles de 5 cm;
- L’utilisateur demande à Diablon d'attaquer Azag, cela n'a aucun effet;
- L’utilisateur demande à Azag d'attaquer Diablon, qui perd 5 points de vitalité;
- ...
Vous avez votre modèle et vos codes, y compris quelques tests.
Pour l'an prochain, ils transportent un sac d'armes?
===== Partie 2 : Fabriques (1 heure) =====
/* Fabrique
*/
==== Gérer la construction des armes ====
Au fur et à mesure que le jeu est mis sur le marché, pour "créer du hype", on veut pouvoir ajouter de nouvelles armes, sans modifier l'ensemble du jeu.
L'idée est de :
- **ne pas modifier les classes d'armes définies précédemment;**
- pouvoir créer des armes à partir de leur type et avec un nom donné (ex: créer une épée de nom //excalibur//);
- retrouver une arme à partir de son nom;
- ne pas dépendre des classes concrètes d'armes lors de la création d'un personnage.
exemple de scenario de test dont vous pouvez vous inspirer:
WeaponFactory wf = new WeaponFactory();
Weapon w = wf.createWeapon("SWORD","excalibur");
Weapon w = wf.getWeapon("excalibur");
Personnage azag = new Orc("Azag",5);
assertEquals(Class.forName("coo.armes.Epee"), azag.getArme().getClass());
(1) Modélisez par un diagramme de séquence les interactions que vous envisagez à la création d'un personnage; complétez le diagramme suivant.{{:2019_2020:s3:concprogobjet:td:capture_d_e_cran_2019-11-19_a_00.14.50.png?200|}}
(2) Complétez sur papier votre diagramme de classe.
(3) "Coder vos modèles". Vous devez comprendre comment votre modèle évolue avant de mettre à jour vos codes.
==== Gérer des usines d'armes ====
On désire à présent pouvoir :
- gérer plusieurs usines d'armes : par exemple, une usine d'armes moderne peut créer toutes les armes initialement prévues plus de nouveaux types d'armes comme des fusils;
- associer à un joueur une usine d'armes qui lui est propre;
- limiter le nombre d'armes créées par une usine;
- centraliser néanmoins toutes les armes créées, c'est à dire que si "excalibur" est créée par la fabrique du joueur 1, le joueur 2 peut quand même accéder à cette arme par sa propre fabrique.
exemples de tests à adapter avec vos codes
WeaponFactory wp = new WeaponFactory();
WeaponFactory wp2 = new ModernWeaponFactory();
String nom = "Ref";
int i = 0;
Arme a1 = wp.creerArme("Epee", nom + i ); i++;
Arme a2 = wp2.creerArme("Dague", nom + i);i++;
Arme a3 = wp2.creerArme("Fusil", nom+ i );i++;
Arme a4 = wp2.creerArme("Missil", nom+ i ); //Type non reconnu
assertTrue(a1 != null);
assertTrue(a2 != null);
assertTrue(a3 != null);
assertTrue(a4 == null);
assertEquals(a1, wp2.getArme(nom+0)); //On retrouve une arme créée par un autre
assertEquals(a3, wp2.getArme(nom+2));
WeaponFactory wp2 = new ModernWeaponFactory(2);//Droit de créer au maximum 2 armes
String nom = "Ref";
int i = 0;
Arme a1 = wp2.creerArme("Epee", nom + i ); i++;
Arme a2 = wp2.creerArme("Fusil", nom+ i );i++;
Arme a3 = wp2.creerArme("Dague", nom + i);
assertTrue(a1 != null);
assertTrue(a2 != null);
assertTrue(a3 == null); //l'arme n'a pas été créée
Il est possible que vous ayez déjà répondu à plusieurs éléments de cette question en répondant au cas précédent.
Avant de coder, à nouveau mettez bien à jour votre modèle de classes
===== Partie 3 : Etats (30min) =====
Un personnage peut passer par différents états.
Au début tous les personnages sont //vivants//.
Un personnage passe dans un état //faible// lorsqu'il n'a plus qu'une force de 25 points de vitalité.
Il est //mort// lorsque ses points de vitalité sont à 0.
Un personnage //faible// lorsqu'il est attaqué, voit sa force protectrice réduite de (1-1/points de vitalité). Ainsi si un personnage faible est attaqué il perdra : - ( (1-1 / ))
Si le personnage est mort et qu'il est attaqué, il ne se passe rien.
//exemple ://
Personnage de vitalité 20 de protection 5 (il est donc faible) s'il est attaqué par une arme de force 10, il perdra : 10 - (5 *(1-5/20) donc 5,25. Ainsi plus il est faible plus il perd de points.
//exemple de tests ://
Au depart il est vivant !
humain de force 25.0 attaque un humain B de vitalité/protection 100.0/5.0
La vitalité resultante de B est de 80.0 et il est toujours Vivant
Apres plusieurs attaques, un humain a une vitalité de 24.0; il est donc Faible
Apres une attaque par une force de 25.0
Sa vitalité résultante est de 3.791666666666668 et il est Faible
**Extension :** Un personnage peut passer dans un état invincible pour un temps donné. Dans ce cas, il ne perd aucun point de vitalité quand il est attaqué.
Au depart il est vivant ! vitalite : 100.0
il est rendu invincible:
Apres une attaque par une force de 25.0
Sa vitalité resultante est toujours de 100.0
(1) Modéliser une attaque par un diagramme de séquence, pour cela complétez le diagramme suivant.
{{:2019_2020:s3:concprogobjet:td:capture_d_e_cran_2019-11-19_a_07.53.38.png?200|}}
(2) Complétez sur papier votre diagramme de classe.
Pour l'an prochain, Reformuler en parlant de recevoir des coups
===== Partie 4 : Compositions de combattants (20 min) =====
On désire pouvoir envoyer au combat des personnages individuels et également des groupes tels que des armées, des bataillons ou des hordes.
Il s'agit donc dans le jeu de pouvoir envoyer différents combattants à l'attaque contre d'autres combattants.
Nous avons déjà vu les attaques entre personnages.
Voici les groupes de combattants que l'on désire manipuler.
* Une horde est composée de personnages.
* Un bataillon est composée de personnages.
* Une armée est composée de généraux (personnages individuels) et de bataillons.
La force d'une attaque
- par une horde est la somme des forces de chacun des personnages qui constituent la horde, multipliée par 0.75.
- par un bataillon est la somme des forces de chacun des personnages qui constituent le bataillon, multipliée par 1,5.
- par une armée est la somme des forces de chacun des éléments (personnages, horde, ...) qui constituent l'armée.
//De même pour la puissance protectrice.//
Attention, ceci n'est qu'un début dans le futur nous associerons aux groupes des véhicules (chars, ...) qui auront leur propre force.
"la force d'un personnage unique" -- 25
"la force d'un bataillon composé de 2 humains" -- (25+25)*1.5 = 75
"la force d'une horde composée de 2 humains" -- (25+25)*0.75 = 37.5
"la force de l'armee composée d'un général, du bataillon et de la horde" -- force du général + force bataillon + Horde " = (25 + 75 + 37.5)
Mettez à jour votre diagramme de classes, PUIS codez.
Pour l'an prochain, explicitez le fait que la force de l'attaque et de la protection sont propagées sur chacun des acteurs..... Pas facile du tout.... probablement un pourcentage
===== Partie 5 : Forces Occultes (30 min) =====
Les "divinités" veulent être informées de toute attaque des personnages, des constructions d'armes.
Elles sont mal connues; certaines divinité peuvent apparaitre ou disparaitre au fil du temps, elles peuvent surveiller certains personnages et pas d'autres, etc.
Voici des exemples de scénario à réaliser.
- Les "Dieux Espions" surveillent des personnages individuellement en affichant à chaque notification d'une attaque par ces derniers : "NomPersonnage nouvelle attaque contre NomPersonnage".
- Les "Dieu des calculs" surveillent des personnages et comptabilisent le nombre d'attaques déclenchées par chacun d'eux. En particulier, le dieu "Calculus" surveillent TOUS les personnages dès leur création.
- En cours de jeux, on peut ajouter de nouveaux Dieux. Ainsi les dieux issus de la "National Rifle Association" ont pour objectifs de signaler (juste un affichage dans le cadre du TD) chaque fois qu'une nouvelle arme est construite.
===== Partie 6 : Une peu de magie dans un monde de brutes =====
Quel est le "pattern" que vous pourriez utiliser dans cet exercice ?? :-/
/*
Decorateur
*/
En fonction du jeu, il est possible d'améliorer les armes en leur associant un amplificateur ou une force protectrice supplémentaire, de coefficient donné (opération plus). Il est également possible d'enchanter une arme ce qui a pour effet de multiplier la puissance de frappe et protectrice par un coefficient fonction du sortilège.
On veut pouvoir ainsi ajouter en fonction des jeux que nous diffuserons différentes manières d'"améliorer" les armes (les rendre invisibles, leur donner une puissance seulement pendant un temps donné, leur puissance dépend de l'état du jeu, etc.)
Ces armes modifiés sont manipulables comme des armes normales.
exemple de scénario :
- On modifie l'arme associée à Diablon en la rendant magique avec un coefficient de 2;
- Diablon attaque Azag qui perd 5 points de vitalité;
- On modifie l'arme associée à Diablon en lui ajoutant un amplificateur de coefficient 3;
- Diablon attaque Azag qui perd 8 points de vitalité;
- ....
//Facultatif :// On aimerait à présent que la force d'une arme soit proportionnelle à la vitalité de l'attaquant et la force protectrice proportionnelle à la vitalité de celui qui reçoit le coup.
**Remarques :**
* que se passe-t-il si un personnage est attaqué avec une force inférieure à sa capacité à se protéger?
* quelles sont les conséquences d'un ordre différent d'applications des "décoration" ?
===== Partie facultative =====
On veut pouvoir créer des personnages et des groupes automatiquement, sans modifier les personnages définis précédemment.
Un joueur a en début de partie 50 points, qu'il dépense en créant des personnages et/ou des groupes.
- La création d'un personnage a un coût : //Humain : 10; Troll : 5; Tauren: 2; etc.//
* * Ainsi //create("Humain", "Arthur",Joueur)// (i) retourne un personnage "Humain", (ii) associe ce personnage au Joueur, et (iii) décrémente la bourse du joueur de la somme correspondante.
- La coût de création d'un groupe dépend du type de groupe :
- pour une Horde, c'est la somme du coût des personnages qui la composent, multipliée par 0.75.
* //CreateGroup("Horde", "Hunts", joueur, "Troll", 50)// crée une horde de 50 Trolls dont le coût pour le joueur est de (50*5)*0.75 = 187.5. Des noms différents sont donnés par défaut à chacun des membres de la Horde.
- pour un bataillon, c'est la somme du coût des personnages qui la composent
* //CreateGroup("Bataillon", "57Rima", joueur, "Tauren", 50)// crée un bataillon de 50 Tauren dont le coût pour le joueur est de (50*2)= 100. Des noms différents sont donnés par défaut à chacun des membres du bataillon.
- Créer une armée est un peu plus complexe.
* //CreateGroup("Armee","ArmeeNapoleon",joueur,"Humain")// crée une armée dont le général est un humain, qui est crée avec un nom par défaut. Son coût est alors du coût de l'humain multiplié par 1.2 soit 12. Il est ensuite possible de créer un nouveau bataillon ou autre et de les lui ajouter sans coût supplémentaire.
//Bien sûr, les personnages se déplacent, les groupes aussi, etc. mais ce sera pour une autre année.//