• Aucun résultat trouvé

R APPORT DE DÉVELOPPEMENT

N/A
N/A
Protected

Academic year: 2022

Partager "R APPORT DE DÉVELOPPEMENT"

Copied!
13
0
0

Texte intégral

(1)

R APPORT DE DÉVELOPPEMENT

POURSUITE DE ROBOTS

(2)

T ABLE DES MATIÈRES

I.A

RBORESCENCE

! 3

II.I

NTERFACES ET IMPLANTATIONS

! 4

‣EXCEPTIONS" 4

‣LES TYPE GÉNÉRIQUES" 6

‣OUTILS" 6

LISTE DʼADJACENCE" 6

DICTIONNAIRE" 7

COORDONNÉES" 7

CHAMPS DʼACCESSIBILITÉ" 7

PLUS COURT CHEMIN" 8

‣CARTE" 9

‣ROBOTS" 10

AUTOMATE" 10

ICÔNE" 10

‣INTERFACE GRAPHIQUE" 11

UNITÉ DE COMMANDE" 11

UNITÉ DE VISUALISATION" 11

‣UNITÉ DE CONTRÔLE" 12

‣EVÉNEMENTS" 13

(3)

I.A

RBORESCENCE

Nous avons compartimenté notre projet à lʼaide de différentes bibliothèques.

De façon explicite vous pouvez remarquer que chacune dʼentres elles gèrent une partie bien distincte du logiciel.

«Display» regroupe lʼensemble des classes permettant de gérer lʼaffichage graphique du logiciel

«Exception» contient lʼensemble des classes permettant de traiter non seulement les erreurs pouvant être engendrées par le programme mais aussi les événements déclenchés par nos algorithmes que lʼont souhaite traiter depuis notre unité de commande.

«Interface» regroupe lʼensemble des interfaces utilisées pour architecturer notre programme.

«Carte» contient une classe du même nom implantant lʼinterface «ICarte», celle-ci reste le point névralgique de notre application.

«Outils» regroupe lʼensemble de nos classes permettant dʼexploiter les structures de données qui ont été élaborées en amont lors de nos phases de test.

«Robots» contient notre proie et prédateur, tout deux utilisant les mêmes interfaces mais avec des implantations différentes.

Par souci dʼaccessibilité nous avons stocké les images représentant nos robots dans la bibliothèque «Icônes».

(4)

II.I

NTERFACES ET IMPLANTATIONS

‣EXCEPTIONS

En JAVA, tout programme comporte des erreurs, même si celui-ci semble fonctionner à merveille.

Chaque programmeur essaye de réduire au minimum le nombre d'erreurs, mais toutes les erreurs ne peuvent pas forcément être prévues.

Les erreurs syntaxiques sont la plupart interceptées lors de la compilation, mais il reste souvent des erreurs "imprévisibles".

Ces erreurs se produisent généralement de façon exceptionnelle, c'est-à-dire suite à une action de l'utilisateur, ou de l'environnement.

Pour traiter les erreurs, Java propose un mécanisme qualifié d'exception, consistant à effectuer les instructions dans un bloc d'essai (le bloc try) qui surveille les instructions. Lors de l'apparition d'une erreur, celle-ci est lancée dans un bloc de traitement d'erreur (le bloc catch, appelé handler d'exception) sous forme d'un objet appelé Exception.

Le bloc de traitement d'erreur va lever (il s'agit du terme technique, certains diront aussi intercepter) l'exception. Le handler d'exception peut alors traiter l'erreur (en signalant l'erreur par exemple, ou en attendant avant de réessayer, ...) puis lancer à nouveau l'exception vers un bloc de plus haut niveau.

Voici la syntaxe type d'une classe gérant des exceptions : class Nom_de_la_classe {

public static void main(String[] args) {

// Instructions inoffensives (affectations, ...);

try {

// Instructions susceptibles de provoquer des erreurs;

}

catch (TypeException e) {

// Instructions de traitement de l'erreur;

}

// Instructions si aucune erreur est apparue;

} }

Si par contre on désire que l'exception soit traitée par les blocs de niveaux supérieurs, il suffit d'inclure à la fin de la série d'instructions contenues dans le bloc catch{} une clause throw, suivie du type de l'exception entre parenthèse puis du nom de l'exception (son handle pour utiliser un terme exact). Ainsi l'exception continuera son chemin...

Nous nous sommes appuyé sur cet outil afin de distribuer les exceptions à lʼunité de contrôle.

Ainsi lorsquʼune case sera déjà occupée par un objet, ou des paramètres de coordonnées seront incorrects par exemple, lʼunité de contrôle traitera lʼexception au sein de sa classe. Ce procédé a pour avantage dʼalléger les tests conditionnels sur classe sensible et de centraliser les procédures dans une zone plus appropriée.

(5)

Si nous prenons comme exemple notre carte, lʼinterface serait décrire comme ceci : public interface ICarte<K extends Object> {

//Ajouter un objet sur la carte

public void ajouter(K pObj, ICoordonnees pCo) throws CaseNonVideException;

//Supprimer un objet sur la carte

public void supprimer(K pObj) throws ElementAbsentException;

// ... ect }

Et son implantation donnerait :

public class Carte implements ICarte<Object> { //Ajouter un objet sur la carte

public void ajouter(K pObj, ICoordonnees pCo) throws CaseNonVideException{

! //Si il y a déjà un objet, on envoie une exception au bloc supérieur if(estOccupe(pCoord))

throw new CaseNonVideException(getElement(pCoord), pCoord);

// ... ect }

// ... ect }

La gestion de cette erreur est en quelque sorte une exigence de lʼinterface qui nous dit «Si vous utilisez cette interface, alors la case ne peut accepter quʼun seul objet par case»

Nous fixons la condition pour laquelle lʼexception sera déclenchée, mais son traitement se fera en amont depuis notre unité de contrôle.

(6)

‣LES TYPE GÉNÉRIQUES

Les génériques (de l'anglais generics) sont des classes qui sont typées au moment de la compilation. Autrement dit, ce sont des classes qui utilisent des typages en paramètres. Ainsi une liste chainée, qui peut contenir des entiers, des chaines ou autres pourra être typée en liste de chaine ou liste d'entier et ceci permettra au programmeur de ne pas écrire systématiquement des transtypages, méthode qui pourrait s'avérée dangereuse, ce sera le compilateur qui vérifiera la cohérence des données.

Lʼutilisation de génériques est simple, notre interface carte en est dʼailleurs dotée : public interface ICarte<K extends Object> {

//Code ...

}

Lors de son implantation nous pourront spécifier quel type dʼobjet la carte peut accueillir comme par exemple ... des robots implantant lʼinterface «IRobot» :

public class Carte implements ICarte<IRobot> { //Code ...

}

‣OUTILS

LISTE DʼADJACENCE

Afin dʼélaborer notre liste dʼadjacence nous avions besoin dʼun tableau de listes chaînées où chaque case du tableau contenait la liste des voisins dʼune case sur la carte.

Il se trouve que JAVA intègre dans lʼune de ses bibliothèques une structure de donnée correspondant à nos besoins, il sʼagit de «LinkedList».

Nos cases peuvent être représentée par des numéros entiers allant de 0 à (nombre de cases -1).

Lʼordre dʼattribution des numéros démarre dʼen haut à gauche et se poursuit suivant le sens de lecture occidental ligne par ligne ...

Voici ce que nous donne le code JAVA de lʼalgorithme : private LinkedList<Integer>[] liste;

private void initListe(int pIndice) { initListesChainees();

for(int i = 0; i < (nbCases-1)/2; i++) { if((i+1)%pIndice != 0){

liste[i].add(new Integer(i+1));

liste[i+1].add(new Integer(i));

liste[nbCases-i-2].add(new Integer(nbCases-i-1));

liste[nbCases-i-1].add(new Integer(nbCases-i-2));

}

if(i < pIndice*(pIndice-2)){

liste[i].add(new Integer(i+pIndice));

liste[i+pIndice].add(new Integer(i));

liste[nbCases-pIndice-i-1].add(new Integer(nbCases-i-1));

liste[nbCases-i-1].add(new Integer(nbCases-pIndice-i-1));

} }

}

private void initListesChainees() { for(int i = 0; i < nbCases; i++) {

liste[i] = new LinkedList();

} }

(7)

Grâce aux types génériques nous spécifions bien que les listes de voisins sont composées dʼentiers ce qui évitera tout problème de type lors de la compilation du code :

private LinkedList<Integer>[] liste;

DICTIONNAIRE

Pour notre dictionnaire, JAVA intègre au sein de ses bibliothèques la classe «HashMap»

permettant dʼajouter des objets sous forme paire «Clé/Valeur», il nous suffira alors dʼajouter un attribut de ce type dans notre carte pour gérer la position de nos objets.

COORDONNÉES

Nous avons besoin de coordonnées entières pour localiser nos cases sur la carte.

Comme notre liste dʼadjacence est composée de numéros de cases il pourra sʼavérer indispensable de transformer nos coordonnées en numéro.

Enfin nous pourrions avoir à effectuer des calculs vectoriels.

Ces affirmations nous ont permis de construire notre interface «ICoordonnees» : public interface ICoordonnees {

//Retourne la valeur en abscisse public int getX();

//Retourne la valeur en ordonnée public int getY();

//Retourne les coordonnées du point/vecteur public int[][] getCoordonnees();

//Calcul les coordonnées d'un vecteur

public ICoordonnees calculVecteur(ICoordonnees pCoordFin);

//Calcul les coordonnées d'un point à l'aide d'un vecteur avec comme origine l'instance courante

public ICoordonnees calculPoint(ICoordonnees pVecteur);

//Converti les coordonnées en numéro de case d'un tableau d'après sa largeur

public int coordonneesEnNumero(int pLargeur);

}

CHAMPS DʼACCESSIBILITÉ

Lʼalgorithme pour créer nos champs dʼaccessibilité reste abstrait mais comme nous lʼavions vu dans lʼanalyse de développement celui-ci prend comme paramètre un pas n entier pour construire une matrice booléenne de taille m * m ou m = 2n + 1.

public static boolean[][] matriceAccessibilite(int pIndice) { boolean[][] acc = new boolean[pIndice*2+1][pIndice*2+1];

for(int i = 0; i < pIndice; i++) { for(int j = 0; j <= i; j++) {

int x = i - pIndice;

int y = -j;

acc[x+pIndice][y+pIndice] = true;

acc[-x+pIndice][-y+pIndice] = true;

acc[-y+pIndice][x+pIndice] = true;

acc[y+pIndice][-x+pIndice] = true;

} }

return acc;

}

(8)

Nos champs de vision et de déplacement seront donc calqués sur le même modèle il suffira alors dʼappeler la méthode permettant de construire la matrice booléenne en question :

public static boolean[][] matriceDeplacement(int pIndice) { return matriceAccessibilite(pIndice);

}

public static boolean[][] matriceVision(int pIndice) { return matriceAccessibilite(pIndice);

}

Les méthodes sont statiques puisquʼil nʼest pas nécessaire de devoir instancier une classe pour les utiliser.

Deux interfaces ont été créées pour répondre au problème du champ de vision et de déplacement :

public interface IDeplacement {

//Retourne une matrice d'accessibilité correspondant à un champs de déplacement

public boolean[][] getMatriceDeplacement();

}

public interface IVision {

//Retourne une matrice d'accessibilité correspondant à un champs de vision public boolean[][] getMatriceVision();

}

Un objet voulant se déplacer ou voir sur la carte devra implanter ces deux interfaces.

PLUS COURT CHEMIN

Lorsque le prédateur détecte sa proie il devient intéressant de calculer le plus court chemin pour la dévorer.

Un célèbre algorithme devient alors incontournable pour répondre à ce problème, son inventeur Edsger Dijkstra, a répondu au problème du plus cours chemin il y a déjà une cinquantaine dʼannée.

Notre algorithme prendra en paramètre : Une liste de cases pouvant être parcouru

Une liste dʼadjacence contenant les voisins des cases Une case de départ

Une case dʼarrivée

Lʼalgorithme du plus court chemin ne pouvant être utilisé que dans le cas dʼun graphe connexe, une exception relevant la non connexité de la liste de case passée en paramètre sera créée.

public Dijkstra(LinkedList<Integer> pListeCases, LinkedList<Integer>[]

pListeAdj, int pDepart, int pArrivee) throws GrapheNonConnexeException;

Une méthode permettant de récupérer le chemin sous forme dʼune liste dʼentier sera implanté : public LinkedList<Integer> getPlusCourtChemin();

(9)

‣CARTE

Notre carte possède lʼensemble des méthodes nécessaires à la gestion dʼobjets sur une grille à deux dimensions. (ajout, suppression, déplacement ... ect ...).

Nous allons vous présenter la méthode permettant de voir si un objet posé sur une case X a accès à une autre case Y par rapport à une matrice dʼaccessibilité

public boolean estAccessible(ICoordonnees pPointDepart, ICoordonnees pPointArrivee, boolean[][] pChamps) {

//Vecteur départ > arrivée

ICoordonnees vecteur = pPointDepart.calculVecteur(pPointArrivee);

//Centre du champs matriciel

ICoordonnees centreChamps = new Coordonnees(pChamps.length/2, pChamps.length/2);

//Coordonnées du point sur la matrice

ICoordonnees coordPoint = Coordonnees.calculPoint(centreChamps, vecteur);

try {

//Le point est-il dans le champs matriciel ? if(pChamps[coordPoint.getX()][coordPoint.getY()])

return true;

return false;

}

//Le point est-il hors de la matrice ?

catch(ArrayIndexOutOfBoundsException pException) { return false;

} }

La puissance des interfaces et des exceptions nous permet de gérer parfaitement notre algorithme.

En effet indépendamment de son implantation, le polymorphisme des interfaces nous permet de passer un objet de type «ICoordonnees» en paramètre.

Si les coordonnées dʼarrivée sortent du champ dʼaccessibilité, une exception est levée nous permettant de retourner lʼétat correspondant.

Qu-en est-il de la vision dʼun objet sur la carte ? Et bien une méthode appelant celle décrite ci- dessus est créée :

public boolean estVisible(IVision pObj1, Object pObj2) { ICoordonnees coordObj1 = coordonnees(pObj1);

ICoordonnees coordObj2 = coordonnees(pObj2);

return estAccessible(coordObj1, coordObj2, pObj1.getMatriceVision());

}

Comme nous lʼavions expliquer plus haut, le polymorphisme des interfaces nous permettent de dire quʼun objet peut regarder autour de lui sur la carte si est seulement si il implante lʼinterface

«IVision».

(10)

‣ROBOTS AUTOMATE

Nos robots sont dotés dʼun système de contrôle autonome. Pour que notre carte détecte si un objet est un automate nous avons créé une interface automate.

Etant donné quʼun automate doit pouvoir regarder et se déplacer sur une carte, lʼinterface

«IAutomate» héritera des interfaces «IVision» et «Ideplacement»

Attention en JAVA lʼhéritage des interfaces nʼa rien à voir avec celui des classes. Il est plutôt question de polymorphisme, si nous créons un objet interface de type «IAutomate» nous aurons accès non seulement aux méthodes de lʼinterface, mais aussi à celles héritées.

public interface IAutomate extends IVision, IDeplacement { //Rejoindre une carte

public void rejoindreCarte(ICarte pCarte, ICoordonnees pCoord);

//Quitter une carte

public void quitterCarte();

//Se faire virer d'une carte public void virer();

//Envoi la carte en cours public ICarte getCarte();

//Se déplacer sur une carte

public void seDeplacer(ICoordonnees pCoord);

} ICÔNE

Pour réaliser lʼaffichage des courses poursuites nous avons utilisé la librairie «SWING/AWT» natif à JAVA.

Le robot implantera alors une interface «IICone» permettant de lui attribuer un icône de type

«ImageIcon». Cette classe permet dʼafficher un icône dans un composant «JLabel».

public interface IIcone { //Renvoi l'icône du pion public ImageIcon getIcone();

//Récupère l'icône du pion

public void setIcone(ImageIcon pIcone);

}

(11)

‣INTERFACE GRAPHIQUE

Lʼinterface graphique développée sous «SWING/AWT» reste simple puisquʼici il nʼest pas question dʼergonomie logicielle mais plutôt dʼun travail poussé sur les structures de données et leurs implantations.

Nous avons créé une interface «IFenetrePrincipale», celle-ci intègre toutes les méthodes permettant dʼafficher les courses poursuites et les statistiques de course mais aussi les paramètres renseignés par lʼutilisateur.

Elle redistribue ensuite les actions à ses composants.

UNITÉ DE COMMANDE

Lʼunité de commande est un formulaire offert à lʼutilisateur du programme qui lui permet de fixer les positions initiales des robots ainsi que le nombre de courses et de tours par courses.

Notre rôle est donc ensuite de récupérer ses informations afin de pouvoir démarrer les courses poursuites, nous avons alors créé une interface «IControleur» :

public interface IControleur {

//Récupère le nombre de courses à faire public int getNbCourses();

//Récupère le nombre de tours à faire public int getNbTours();

//Récupère les coordonnées initiales du robot public ICoordonnees getCoordRobot();

//Récupère les coordonnées initiales du prédateur public ICoordonnees getCoordPred();

}

UNITÉ DE VISUALISATION

Lʼunité de visualisation regroupe lʼaffichage des statistiques et des courses poursuites.

Nous avons alors une interface permettant de modifier lʼétat des statistiques «IInformations» : public interface IInformations {

//Envoi le nombre de courses effectuées public void setNbCourses(int pIndice);

//Envoi le nombre de tours effectués public void setNbTours(int pIndice);

//Envoi le nombre de tours moyens

public void setNbToursMoyen(int pIndice);

//Envoi le taux d’échecs

public void setTauxEchecs(int pIndice);

}

(12)

Ainsi quʼune interface «IGrille» permettant de positionner les objets implantant lʼinterface

«IICone» sur la grille de visualisation : public interface IGrille {

//Ajouter un pion sur une case

public void ajouter(IIcone pPion, ICoordonnees pCoord);

//Supprimer un pion sur une case

public void supprimer(ICoordonnees pCoord);

//Déplacer un pion d'une case à une autre

public void deplacer(ICoordonnees pCoordInit, ICoordonnees pCoordFin);

//Vider toutes les cases de la grille public void vider();

//Vérifie si les coordonnées sont accessibles sur la carte public boolean estAccessible(ICoordonnees pCoord);

//Vérifie si une case est occupée

public boolean estOccupe(ICoordonnees pCoord);

//Récupère une case en fonction des coordonnées public ICase getCase(ICoordonnees pCoord);

//Récupère une case en fonction de son numéro d'ordonnancement public ICase getCase(int pNumero);

//Nombre de pions sur la grille public int getNbElements();

//Nombre de lignes

public int getNbLignes();

//Nombre de colonnes

public int getNbColonnes();

//Nombre de cases

public int getNbCases();

}

Nous avons fait le choix dʼimplanter dans les cases de notre grille une interface «ICase» qui aura pour rôle de gérer lʼajout, la suppression ou la récupération dʼun objet implantant lʼinterface

«IIcone» dans une case :

public interface ICase { //Ajoute le pion

public void ajouterPion(IIcone pPion);

//Supprime le pion

public void supprimerPion();

//Récupère le pion

public IIcone getPion();

}

‣UNITÉ DE CONTRÔLE

Lʼunité de contrôle gère le déroulement des courses poursuites, elle échange les informations avec la carte, les robots et la fenêtre principale.

A ce titre lorsque nous lançons notre programme, nous instancions notre classe «Controle» qui prend en paramètre une carte de type «ICarte» et une fenêtre principale de type

«IFenetrePrincipale».

Mais lorsque lʼutilisateur souhaite lancer une course, il faut pouvoir notifier notre unité de contrôle de cet événement.

public Controle(IFenetrePrincipale pFenetre, ICarte pCarte) { //Code ...

}

(13)

‣EVÉNEMENTS

Pour notifier notre interface de contrôle dʼune action de lʼutilisateur nous allons créer une interface

«IFenetrePrincipaleListener» qui sera implantée dans notre unité de contrôle.

public interface IFenetrePrincipaleListener extends EventListener { //L'utilisateur clique sur le bouton de validation

public void clicSurDemarrer(IFenetrePrincipale pFenetre);

//L'utilisateur clique sur le bouton d'arrêt

public void clicSurArreter(IFenetrePrincipale pFenetre);

}

Nous transformons notre unité de contrôle en écouteur dʼévénement. Il nous reste maintenant à rajouter un attribut dans notre classe «FenetrePrincipale» :

private EventListenerList listeners;

«EventListenerList» permet de contenir une liste dʼobjets de type «EventListener». Il nous faut alors la possibilité dʼajouter/supprimer/récupérer des écouteurs à notre fenêtre principale :

//Interface IFenetrePrincipaleListener

public void addListener(IFenetrePrincipaleListener pListener) { listeners.add(IFenetrePrincipaleListener.class, pListener);

}

public void removeListener(IFenetrePrincipaleListener pListener) { listeners.remove(IFenetrePrincipaleListener.class, pListener);

}

public IFenetrePrincipaleListener[] getListeners() {

return listeners.getListeners(IFenetrePrincipaleListener.class);

}

Il suffit ensuite de créer des méthodes appelant nos écouteurs afin de distribuer lʼévénement : //Méthodes d'événements

private void fireClicSurDemarrer() {

for(IFenetrePrincipaleListener listener : getListeners()) listener.clicSurDemarrer(this);

}

private void fireClicSurArreter() {

for(IFenetrePrincipaleListener listener : getListeners()) listener.clicSurArreter(this);

}

Références

Documents relatifs

24. L’État a essentiellement trois grandes tâches à accomplir pour assurer la transformation économique de l’Afrique: planifier le processus de développement,

Le choix d’adopter l’approche de l’État développe- mentiste dérive des déficiences constatées des modèles de développement précédents. Ainsi, avant de mettre en

la nature pour ne citer qu’elles et les sciences humaines, qui ont pour objet le comportement de l’Homme au sens large, dans la dimension individuelle et collective.. Toutefois,

Le « criminel », pas plus que la victime, n’est qu’affaire de droit ; si celui-ci énonce, en conséquence d’un usage toujours instable, la criminologie, a à faire,

Apparemment, quoique la question reste floue, il ne s'agit plus d'avancer des motifs plausibles pour lesquels les concubins renoncent à se marier, mais d'expliquer pourquoi

En milliers d'euros I - OPERATIONS LIEES A L'ACTIVITE OPERATIONNELLE Résultat net consolidé y compris participations ne donnant pas le contrôle Dotations nettes aux amortissements

Arrêté semestriel au 30-06-2010 en milliers d'euros Produits des autres activités Produits des autres activités -Plus-values de cessions -Plus –value latente immeubles de placement

Arrêté semestriel au 30-06-2010 en milliers d'euros Produits des autres activités Produits des autres activités -Plus-values de cessions -Plus –value latente immeubles de placement