Simulation de Nuées

De $1

Initiateurs : Michel Buffa, Richard Grin, Philippe Collet
Version initiale du code: Philippe Collet
Adaptation et mise à jour: Philippe Renevier-Gonin, Frédéric Mallet

Introduction

L'application fournit une architecture pour faire des simulations de "bébêtes". Ces bébêtes évoluent dans un espace clos, le simulateur doit permettre de voir leurs déplacements. Une bébête a donc une position, elle se déplace dans une certaine direction avec une vitesse donnée et elle peut voir devant elle avec un certain champ de vision. Elle est censée agir en fonction de son environnement (les bébêtes aux alentours). Vous disposez d’une version de base du projet (code source) et d’un jar permettant d’essayer le résultat final. Vous disposez de deux séances pour réaliser ce tp. A la fin de la première séance, vous devriez être dans la partie IV sur la fabrique de fabriques.

Pour conserver l’évolution du tp, vous pouvez faire pour chaque partie un projet différent dans votre IDE préféré.

Partie I - Prise en main des bébêtes

 Temps estimé: 40 minutes

Vous disposez d’un code de départ qui contient un projet Eclipse:

  • Sous Eclipse, File/Import...
  • Select Existing projects into workspace..., Next
  • Select Archive File and browse to select the .zip file
  • Select "Bebets 01 Debut" and Finish 

Essayons de mettre à plat ensemble les entités simulées à partir du code que nous avons au départ. On peut déjà distinguer un certain nombre de rôles différents dans une bébête:  

  • c'est une entité graphique qui se dessine avec une certaine couleur ;  
  • c'est une entité qui a une certaine position dans un champ ;  
  • c'est une entité qui se déplace et se dirige (vitesse et direction), on distinguera position et déplacement, car on pourrait vouloir mettre des entités positionnées mais inertes dans notre simulateur ;  
  •  c'est une entité qui agit, ou tout du moins qui s'actionne, puisque c'est le "moteur de simulation" qui la fait agir...  

Un champ de bébêtes est l’entité qui « porte » les bébêtes :  

  • un champ possède une certaine dimension ;
  • un champ contient des entités positionnables  ;
  • il y a un moteur de simulation avec un thread qui provoque des pas de simulation et actionne les objets "actionnables" à chaque pas ; 

il y a aussi un moteur d'affichage, plutôt mal fichu, car entièrement couplé avec le moteur de simulation à coup de méthode repaint, alors qu'il serait beaucoup mieux d'avoir un second moteur (donc un second thread) pour la visualisation, indépendamment de la simulation

Question 1

Complétez les diagrammes suivants pour représenter le simulateur. Les diagrammes sont séparés en deux par commodités.

Bebetes.png

champ.png

Question 2

Décrivez en français le comportement des "BebeteEmergente"s et des "BebeteHasard"s.

Question 3

Quelle(s) classe(s) faut-il modifier pour :

  • "nommer" une seule bébête parmi toutes afin de la repérer plus facilement ?
  • associer une image à chaque bébête ?
  • ajouter un nom à toutes les bébêtes ?

Pour les trois questions ci-dessus, on répondra dans le cas où l'on souhaite faire ces actions pour les bébêtes émergentes seulement, pour les bébêtes hasard ou pour toutes les bébêtes indifféremment.  

Est-ce que ces actions sont liées à la spécificité (émergente ou hasard) de la bébête ?

Question 4

Comment feriez-vous pour changer la quantité, le type ou les proportions des bébêtes présentes dans le champ ?

NB: ne perdez pas de temps avec les méthodes utilitaires de calcul de direction et de distance, qui sont dans une classe utilitaire. 
Ne perdez pas de temps avec la classe de lancement de programme « bebetes.LanceBebetes ». 
Ne prenez pas en considération (pour l’instant) les tests unitaires.

Les étapes suivantes apporteront des réponses basées sur les patrons de conception, afin de rendre le code plus évolutif (meilleure structuration du code).

 

Partie II - Patron de structure: décorateur

  Temps estimé: 60 minutes

Pour répondre à la Question 3, puisqu’il s’agit d’ajouts non fonctionnels, et plutôt que de faire beaucoup d’héritage (nombre de décorations x nombre de types de bébêtes), vous allez appliquer le patron Décorateur. Procéder en cinq étapes : 

Question 5

Créez un décorateur de Bebete qui n’ajoute rien mais qui ne modifie rien. Comme le décorateur devra se comporter comme une Bebete, votre IDE devrait vous aider en générant des méthodes pour vous. Cependant, la classe abstraite Bebete n’est peut-être pas assez détaillée (il pourrait manquer des méthodes pour permettre la substitution d’une Bebete par sa décoratrice).

Question 6

Essayez votre décorateur (vous ne devez pas voir de différence). Pour introduire une ou plusieurs bébêtes décorées, il vous faudra modifier la méthode fabriqueBebetes de la classe ChampDeBebetes.

Question 7

Dupliquez le test unitaire TestOrdre en changeant les bébêtes b1 et b2 par des décoratrices de b1 et b2. Vous devriez constater un problème lié à la méthode getChosesVues de PerceptionAble.

Pour exécuter un test unitaire dans Eclipse, cliquez bouton droit sur le test à exécuter, "Run as" ..., JUnit Test. Il faut avoir ajouté dans le "Build Path" la bibliothèque JUnit4 pour que le test compile !

Question 8

Après avoir corrigé la méthode getChosesVues de PerceptionAble, implanter deux décorateurs "concrets" : 

  1. Un qui ajoute (par décoration) un nom aux bébêtes et qui le dessine ;
  2. Un autre qui affiche un écho autour des bébêtes (cercle coloré qui s'agrandit à chaque pas de simulation).

Voir le résultat attendu à la fin de la partie II.

Partie III - Patron de création: Usine/Fabrique

   Temps estimé: 40 minutes

Nous appellerons écosystème un milieu capable de générer des bébêtes. Au moment de créer le champ de bébêtes, nous devrons préciser quel écosystème nous souhaitons "simuler". Nous considérerons 3 écosystèmes :  

  • La nuée (100% de bébêtes émergentes) 
  • L'aléatoire (100% de bébêtes hasards) 
  • Le mixte (x% de bébêtes hasards et (100-x)% de bébêtes émergentes) 

Ces trois écosystèmes seront trois fabriques. Une fabrique sera utilisée par ChampDeBebetes. Modifiez cette classe en conséquence, avec un impact notamment sur la méthode fabriqueBebetes. 

Question 9

Définir une interface BebeteFactory et implanter les 3 écosystèmes en tant que fabriques.

L'usine à utiliser est choisie lors de la construction du champ de bébêtes.

Partie IV - Fabrique de Fabriques

Temps estimé: 40 minutes

En pratique, le champs de bébêtes n'a pas besoin de connaître l'usine concrète qui est utilisée. Il devrait également être possible de changer d’écosystème en cours d’exécution et de régénérer la liste des bébêtes. Pour cela il faut utiliser une fabrique de fabriques.

Une fabrique de fabriques est une classe (EcosystemFactory) qui possède une méthode getEcosystem pour créer une BebeteFactory. L'utilisateur ne connaît pas la classe concrète utilisée. Le critère de construction de la fabrique peut être arbitraire (Random) ou peut dépendre d'un paramètre de la méthode getEcosystem.

Question 10

Créer la classe EcosystemFactory et sa méthode getEcosystem. Le choix de l'écosystème produit dépendra du choix fait par l'utilisateur dans une liste déroulante (JComboBox). Cette classe connaît toutes les fabriques concrètes disponibles et les offres à l'utilisateur. Lorsqu'une solution est choisie, le champs de bébêtes est modifié pour prendre en compte la nouvelle usine. Les bébêtes existantes sont détruites et de nouvelles bébêtes sont construites à partir de la nouvelle BebeteFactory. La classe ChampDeBebetes ne doit jamais connaître le type concret utilisé !

Voir le résultat attendu à la fin de la partie IV.

Lors des questions suivantes, quand vous ajouterez des nouvelles fabriques, il faudra modifier la fabrique de fabriques en conséquence.

Partie V - Patron de comportement : Stratégie

Temps estimé: 60 minutes

Partons de l'hypothèse qu’une bébête BebeteEmergente pourrait avoir « un accident » et se comporter dès lors comme une bébête BebeteHasard. A l’inverse, les hasards de l’évolution peuvent créer quelques bébêtes BebeteHasard avec un comportement grégaire. Ainsi le comportement coder en dur dans les classes BebeteHasard et BebeteEmergente pourrait être changé : (i) à la création d’une bébête et/ou (ii) à l’exécution. L’objectif est donc d’encapsuler le comportement, le sortir de la classe Bebete et l’abstraire pour pouvoir en proposer des implantations différentes. Il s’agit là du patron de comportement Strategie. 

Voici l’abstraction proposée : 

package bebetes.strategy;

import bebetes.Bebete;

public interface Strategy {
  public void calcule(Bebete bebete);

  public void applique(Bebete bebete);
 }

Question 11

Expliquer cette abstraction.

Question 12

Appliquer le patron de conception Stratégie qui permet de coder le comportement (Emergent ou Hasard) dans une classe qui réalise l'interface Strategy. Le comportement d'une bébête peut alors être modifié dynamiquement en cours d'exécution.

Répercuter les modifications sur le reste du projet (les usines notamment).

Vérifier que les tests unitaires fonctionnent encore.

Question 13

La classe Bebete doit-elle encore être abstraite ? Sinon, comment faut-il la modifier ?

Peut-on alors se passer des classes BebeteEmergente et BebeteHasard.

Question 14

Ajouter une stratégie EmergenteFiltrant : elle fonctionne comme la stratégie initiale des BebeteEmergente, mais seules les bébêtes du même type (même classe ou sous-classe) sont considérées pour déterminer la direction et la vitesse. En effet, les canards suivent les canards et non pas les hirondelles.


Faire un écosystème fait de bébétes émergentes avec cette stratégie et quelques bébêtes « hasard ».


Votre réponse à la question précédente a-t-elle changée ? 

Partie VI - Patron de structure : Adaptateur

 Temps estimé: 60 minutes

Votre projet a connu une branche il y a quelques temps. Et une autre personne a développé des stratégies autrement. Cette branche contient quelques erreurs de conceptions et arrive à un cul de sac, notamment car la simulation n’est pas très bien gérée. Cependant elle contient une stratégie qui vous intéresse : la Fuite. Vous ne voulez pas tout recoder, alors vous allez devoir adapter ce qui a été fait à votre projet. Vous disposez d’un jar (qui inclue une javadoc minimale) de la branche différente : versionparallele.jar .
(configurez votre IDE pour que cette javadoc soit accessible, dans eclipse c’est dans « Project > Properties > Java Build Path > Libraries ) 

La classe culdesac.Fuite hérite de culdesac.Comportement (classe qui définit la « stratégie »). Cependant, elle n’a qu’une seule méthode :  

/**
 * en param : il faut la liste des voisins et qui on est
 * agit calcule l'état à  l'instant suivant (x, y, vitesse, orientation)
 * il faut donc renvoyer ses quatre valeurs sous forme d'un Etat
 * Limitation/Défaut de conception : appel non maitrisé, il faut que params soit : distanceMin, vitesseMax, Class de filtrage 
 * @param list liste des bebetes autour (type culdesac.MaBebete)
 * @param etatCourant position, vitesse et direction (culdesac.Etat) de la
 * @param params appel non maitrisé, il faut que params soit : distanceMin, vitesseMax, Class de filtrage
 * @return le futur état (position, vitesse et direction )
 */
public Etat agit(ArrayList<MaBebete> list, Etat etatCourant, Object... params)

La classe culdesac.Fuite n’utilise que le second param (params[1]) comme vitesse maximum à ne pas dépasser (lors de la fuite). 

Les bébêtes sont des instances (via des classes concrètes) de la classe abstraite culdesac.MaBebete. La position actuelle et celle future sont stockées dans une classe « structure » culdesac.Etat :

package culdesac;

public class Etat {
  public Etat() { }

  public Etat(Etat toClone) {
    x = toClone.x;
    y = toClone.y;
    vitesse = toClone.vitesse;
    direction = toClone.direction;
  }

  public double x, y;

  public double vitesse, direction;
} 

Précisons également que les classes culdesac.ChampDeBebetes et bebetes.ChampDeBebetes sont différentes.

Question 15

Créer une nouvelle stratégie Peureuse qui sera une simple adaptation du comportement culdesac.Fuite. Vous n'avez pas besoin du code de culdesac.Fuite pour faire cela! 

Il vous faudra pour recréer des objets, dont certains seront « artificiels » (comme un champ dont les dimensions devront correspondre au « vrai » champ), d’autres seront des projections de votre système à l’ancien (les bébêtes). Par contre, il ne doit y avoir qu’une seule simulation…

Question 16

Créer un nouvel écosystème avec quelques bébêtes peureuses. Décorez les pour les reconnaître!

Question 17

Créer une nouvelle stratégie « Agoraphobe » : s’il y a moins de 10 bébêtes en vue (liste des choses vues), alors le comportement est « Emergent » s’il y en a plus, le comportement est la Fuite…

Créer un nouvel écosystème avec quelques bébêtes Agoraphobes (décorez les pour les distinguer des autres)

Voir le résultat attendu à la fin de la partie VI.