Focus sur les associations

Classe : Focus sur les associations

Dans de TD, nous allons nous intéresser à un exemple un peu plus complexe de classes fortement liées les unes aux autres. À partir d'un problème qui va nous paraître simple, nous allons le modéliser, l 'implémenter et le tester. Lors de la modélisation, nous allons déterminer quelles sont les bonnes associations entre quelles classes. Il y a peu de classes, mais elles sont fortement liées. Voici le sujet :

Description initiale : Gestion de processus

On désire gérer des processus.


Un processus se définit par un suite d'étapes :

  • Démarrer le processus revient à démarrer la première étape.
  • Avancer dans le processus consiste à arrêter l'étape courante et à démarrer l'étape suivante.
  • Arrêter le processus consiste à arrêter l'étape courante et à considérer le processus comme terminé, i.e. il ne peut plus avancer, par contre on peut le redémarrer.
  • Le temps pris par un processus pour s'exécuter correspond à la somme des temps pris par chaque étape.
Un modèle de processus se définit comme une suite de modèles d'étapes. Un modèle d'étape est défini par un intitulé par exemple “Aller dans le jardin magique”, un temps prévu par exemple “3s”.
Le temps moyen prévu par un processus est la somme des temps prévus par chacun des modèles d'étapes.
A partir d'un modèle de processus on crée un processus. A partir d'un modèle d'étape on crée un étape.

Afficher une étape revient à afficher l'intitulé du modèle d'étape qui lui est associé. On peut comparer le temps réalisé par une étape avec le temps prévu. On peut comparer le temps réalisé par un processus avec le temps prévu par son modèle.


On peut savoir parmi les processus correspondant à un modèle celui qui a fait le meilleur temps, c'est pour cela que nous associons le nom d'une personne à un processus.

Passer la souris sur une des informations pour en voir un élément de modélisation.

À partir de ces informations nous serrions donc apte à modéliser ce système de gestion de processus, un exemple de tests nous facilite également la tâche :

Voici votre défi :
  • Aller dans le jardin Magique en 5000s
  • Trouver le trésor du Dragon en 5000s
  • Tuer le dragon en 5000s
  • Ramener le tresor au chateau des Neiges en 5000s
A vous de jouer
Aller dans le jardin Magique==> Taper quand fini

4081.0/5000
Trouver le trésor du Dragon==> Taper quand fini

478.0/5000
Tuer le dragon==> Taper quand fini

570.0/5000
Voici votre temps : 5129
-->5129
Meilleur temps :5129
Voulez-vous rejouer Y/N
Y
A vous de jouer
Aller dans le jardin Magique==> Taper quand fini

1652.0/5000
Trouver le trésor du Dragon==> Taper quand fini

580.0/5000
Tuer le dragon==> Taper quand fini

1447.0/5000
Voici votre temps : 3679
-->5129
-->3679
Meilleur temps :3679
Voulez-vous rejouer Y/N
Y
A vous de jouer
Aller dans le jardin Magique==> Taper quand fini

698.0/5000
Trouver le trésor du Dragon==> Taper quand fini

851.0/5000
Tuer le dragon==> Taper quand fini

992.0/5000
Voici votre temps : 2541
-->5129
-->3679
-->2541
Meilleur temps :2541
Voulez-vous rejouer Y/N

Voici donc le diagramme de classe correspondant au système :

En vue d'exécuter le défi, nous allons créer une petite classe de test qui nous permettra de lancer le défi :

public class T_Defi {

    public static void main(String [] args) {
        Defi defi1 = new Defi();
        defi1.startDefi();
    }
}

Ensuite, effectuons le plus facile : générer la structure des classes grâce à Visual Paradigm :

public class Defi {

	private ArrayList<Long> durees;
	private Processus p1;

	public Defi() {
		// TODO - implement Defi.Defi
		throw new UnsupportedOperationException();
	}

	public void startDefi() {
		// TODO - implement Defi.startDefi
		throw new UnsupportedOperationException();
	}

}
public class Etape {

	private String nom;
	private double dureeTh;
	private double dureeReelle;
	private long startTime;
	private long endTime;

	public Etape() {
		// TODO - implement Etape.Etape
		throw new UnsupportedOperationException();
	}

	public Etape(String unNom, double uneDuree) {
		// TODO - implement Etape.Etape
		throw new UnsupportedOperationException();
	}

	public long startEtape() {
		// TODO - implement Etape.startEtape
		throw new UnsupportedOperationException();
	}

	public void displayTime() {
		// TODO - implement Etape.displayTime
		throw new UnsupportedOperationException();
	}

}

public class Processus {

	private long dureeTotal;
	private int etapeCourante;
	private ArrayList<Etape> etapes;

	public Processus(ArrayList<Etape> lesEtapes) {

	}

	public void addEtape(Etape uneEtape) {
		// TODO - implement Processus.addEtape
		throw new UnsupportedOperationException();
	}

	public void startProcess() {
		// TODO - implement Processus.startProcess
		throw new UnsupportedOperationException();
	}

	public long avancerProcesss() {
		// TODO - implement Processus.avancerProcesss
		throw new UnsupportedOperationException();
	}

	public void afficherTemps() {
		// TODO - implement Processus.afficherTemps
		throw new UnsupportedOperationException();
	}

	public int nbEtapes() {
		// TODO - implement Processus.nbEtapes
		throw new UnsupportedOperationException();
	}

}

Bien, commençons par le plus primitif : l'étape, ses attributs sont donc sont nom, sa durée théorique, sa durée réelle et enfin son temps de début et de fin :

public Etape() {
        nom="";
        dureeTh = 0;
        dureeReelle = 0;
    }

    public Etape(String unNom, double uneDuree) {
        nom = unNom;
        dureeTh = uneDuree;
        dureeReelle = 0;
    }

Ensuite, définissons la méthode startEtape, qui s'occupe d'afficher le nom de l'étape et de calculer le temps que mettra l'utilisateur pour la finir, chose simulé par l'appui de la touche Entrée :

public long startEtape() {
        startTime = System.currentTimeMillis();
        System.out.println(nom+"==> Taper quand fini");
        Scanner s = new Scanner(System.in);
        String str = s.nextLine();
        if(str.equals(""))
        {
            endTime = System.currentTimeMillis();
            long tempsCourant = (endTime - startTime);
            displayTime();
            return tempsCourant;
        }
        return 0;

    }

Une fois que l'utilisateur aura appuyé sur la touche, l'étape se charge d'afficher le temps passé comparé a la durée théorique de l'étape :

public void displayTime() {
        System.out.println(endTime - startTime+"/"+dureeTh);
    }

Notre classe Etape est prête. Occupons nous de l'étape suivante, la classe Processus, qui est donc un ensemble d'étape avec sa durée totale. Un petit indice pour savoir l'étape courante est aussi présente, définissons les constructeurs :

public Processus() {
        etapes = new ArrayList<Etape>();
    }
    
    public Processus(ArrayList<Etape> lesEtapes) {
        etapes = new ArrayList(lesEtapes);
    }

Une petite méthode pour ajouter l'étape, au cas où l'on créait le Processus dans étapes initiales :

    public void addEtape(Etape uneEtape) { etapes.add(uneEtape); }

Continuons, une méthode pour démarrer le processus, initialisant les compteurs à zéro :

public void startProcess() {
        dureeTotal = 0;
        etapeCourante = 0;
    }

La méthode la plus importante, celle pour avancer dans la réalisation du processus, correspondant au déclenchement de l'étape suivante dans la liste des étapes du processus. Également, s'il s'avère être la dernière étape, après réalisation de celle ci, le processus se charge d'afficher la durée totale de sa réalisation, obtenu par l'addition des durées de chaque étape.

public long avancerProcesss() {
        Etape monEtape = etapes.get(etapeCourante);
        long duree = monEtape.startEtape();
        dureeTotal+=duree;
        etapeCourante++;
        if (etapeCourante == etapes.size()) afficherTemps();
        return duree;
    }

Et donc, une fois la dernière étape effectué, nous nous occupons d'afficher le temps pris par le processus :

public void afficherTemps()  {
        System.out.println("Voici votre temps:"+dureeTotal);
    }
	

Et enfin, au cas où, un petit accesseur du nombre d'étapes que compose le processus

public int nbEtapes() { return etapes.size(); }

Parfait, notre classe Processus à l'air d'être prête, maintenant, dernière partie : le défi, qui permettra d'exécuter le processus autant de fois que l'utilisateur le souhaite, et se chargeant d'enregistrer le temps pris par chaque processus pour en afficher le plus rapide. Voici le constructeur du défi, s'occupant de construire lui même le processus :

public Defi() {
        durees = new ArrayList<Long>();
        Etape etape1 = new Etape("Aller dans le jardin Magique", 5000);
        Etape etape2 = new Etape("Trouver le trésor du Dragon", 5000);
        Etape etape3 = new Etape("Tuer le dragon", 5000);
        Etape etape4 = new Etape("Ramener le tresor au chateau des Neiges", 5000);
        ArrayList<Etape>etapes = new ArrayList<Etape>();
        etapes.add(etape1);
        etapes.add(etape2);
        etapes.add(etape3);
        etapes.add(etape4);
        p1 = new Processus(etapes);

    }

Et voici la méthode startDefi(), qui s'occupe de lancer le processus tant que l'utilisateur le décide (par un appuie sur la touche y). Après chaque processus exécuté, la liste des temps est affiché, puis le meilleur temps de la session :

public void startDefi()
    {
        String str = "y";
        while(str.equals("y"))
        {
            p1.startProcess();
            long dureeProcesss = 0;
            for (int i = 0; i<p1.nbEtapes(); i++)
                dureeProcesss += p1.avancerProcesss();
            durees.add(dureeProcesss);

            for(int i=0; i<durees.size(); i++)
                System.out.println("--> "+durees.get(i));

            System.out.println("Meilleur temps: "+Collections.min(durees));
            System.out.println("Rejouer y/n?");
            Scanner s = new Scanner(System.in);
            str = s.nextLine();
        }
    }

Nous n'avons plus qu'a tester, grâce à la méthode startDefi(), le résultat semble bien conforme à nos attentes :

Aller dans le jardin Magique==> Taper quand fini

4049/5000.0
Trouver le trésor du Dragon==> Taper quand fini

628/5000.0
Tuer le dragon==> Taper quand fini

562/5000.0
Ramener le tresor au chateau des Neiges==> Taper quand fini

563/5000.0
Voici votre temps:5802
--> 5802
Meilleur temps: 5802
Rejouer y/n?
y
Aller dans le jardin Magique==> Taper quand fini

541/5000.0
Trouver le trésor du Dragon==> Taper quand fini

517/5000.0
Tuer le dragon==> Taper quand fini

450/5000.0
Ramener le tresor au chateau des Neiges==> Taper quand fini

664/5000.0
Voici votre temps:2172
--> 5802
--> 2172
Meilleur temps: 2172
Rejouer y/n?

Voici le code complet des trois classes :


import java.util.*;

public class Defi {
    private ArrayList<Long> durees;
    private Proccesus p1;

    public Defi()
    {
        durees = new ArrayList<Long>();
        Etape etape1 = new Etape("Aller dans le jardin Magique", 5000);
        Etape etape2 = new Etape("Trouver le trésor du Dragon", 5000);
        Etape etape3 = new Etape("Tuer le dragon", 5000);
        Etape etape4 = new Etape("Ramener le tresor au chateau des Neiges", 5000);
        ArrayList<Etape>etapes = new ArrayList<Etape>();
        etapes.add(etape1);
        etapes.add(etape2);
        etapes.add(etape3);
        etapes.add(etape4);
        p1 = new Proccesus(etapes);

    }

    public void startDefi()
    {
        String str = "y";
        while(str.equals("y"))
        {
            p1.startProcces();
            long dureeProccess = 0;
            for (int i = 0; i<p1.nbEtapes(); i++)
                dureeProccess += p1.avancerProccess();
            durees.add(dureeProccess);

            for(int i=0; i<durees.size(); i++)
                System.out.println("--> "+durees.get(i));

            System.out.println("Meilleur temps: "+Collections.min(durees));
            System.out.println("Rejouer y/n?");
            Scanner s = new Scanner(System.in);
            str = s.nextLine();
        }
    }
}

public class Proccesus {
    private long dureeTotal;
    private int etapeCourante;
    private ArrayList<Etape> etapes;

    public Proccesus()
    {
        etapes = new ArrayList<Etape>();
    }
    public Proccesus(ArrayList<Etape> lesEtapes) {
        etapes = new ArrayList(lesEtapes);
    }

    public void addEtape(Etape uneEtape)
    {
        etapes.add(uneEtape);
    }

    public void startProcces()
    {
        dureeTotal = 0;
        etapeCourante = 0;
    }

    public long avancerProccess()
    {
        Etape monEtape = etapes.get(etapeCourante);
        long duree = monEtape.startEtape();
        dureeTotal+=duree;
        etapeCourante++;
        if (etapeCourante == etapes.size()) afficherTemps();

        return duree;
    }

    public void afficherTemps()
    {
        System.out.println("Voici votre temps:"+dureeTotal);

    }
    public int nbEtapes()
    {
        return etapes.size();
    }


}

public class Etape {
    private String nom;
    private double dureeTh;
    private double dureeReelle;
    private long startTime;
    private long endTime;

    public Etape() {
        nom="";
        dureeTh = 0;
        dureeReelle = 0;
    }

    public Etape(String unNom, double uneDuree)
    {
        nom = unNom;
        dureeTh = uneDuree;
        dureeReelle = 0;
    }
    public long startEtape()
    {
        startTime = System.currentTimeMillis();
        System.out.println(nom+"==> Taper quand fini");
        Scanner s = new Scanner(System.in);
        String str = s.nextLine();
        if(str.equals(""))
        {
            endTime = System.currentTimeMillis();
            long tempsCourant = (endTime - startTime);
            displayTime();
            return tempsCourant;
        }
        return 0;

    }
    public void displayTime()
    {
        System.out.println(endTime - startTime+"/"+dureeTh);
    }
}