Modélisation et codage

Introduction

L'objectif de ce travail dirigé est de faire un lien direct entre la modélisation des classes et les codes correspondant. Nous allons voir comment passer d'une classe modélisé sous Visual Paradigm à un fichier Java correspondant. C'est une fonction très utile car elle permet de gagner un temps considérable lors de la conception de système complexe. À condition bien sûr que la conception fut rigoureuse et exempt d'erreur.

Classe : Code et modélisation

Nous allons travailler sur la conception de plusieurs outils de jardinage.

Classe "TailleHaie"

Il nous est indiqué les informations suivantes :

Un taille haie est caractérisé par sa cadence de coupe, typiquement 4500 coupes/minute.
  • switchOn()+: méthode qui allume l’outil et fixe la cadence à 4500.
  • switchOff()+: méthode qui éteint l’outil et fixe la cadence à 0.

La représentation en UML de la classe est rapide :


Visual Paradigm nous permet donc de générer le code Java (et C++) de la classe TailleHaie :


Le code généré est le suivant :

public class TailleHaie {

	private int cadence;
	private boolean etat;

	public int getCadence() {
		// TODO - implement TailleHaie.getCadence
		throw new UnsupportedOperationException();
	}

	public void setCadence(int c) {
		// TODO - implement TailleHaie.setCadence
		throw new UnsupportedOperationException();
	}

	public void switchOn() {
		// TODO - implement TailleHaie.switchOn
		throw new UnsupportedOperationException();
	}

	public void switchOff() {
		// TODO - implement TailleHaie.switchOff
		throw new UnsupportedOperationException();
	}

	public String toString() {
		// TODO - implement TailleHaie.toString
		throw new UnsupportedOperationException();
	}

}

En premier lieu, il faut définir le constructeur qui initialisera la classe TailleHaie dans l’état éteint, avec une cadence de 0 :

public TailleHaie() { 
	cadence = 0;
	etat = false;
}
À noter : les exceptions levées dans le corps des méthodes générées permettent de signaler les méthodes encore non implémentées.

Enfin, on veut pouvoir connaître la cadence du TailleHaie, donc il suffit simplement de définir un accesseur pour l'attribut :

public int getCadence() { return cadence; }

La classe avec ses méthodes définies est la suivante :

public class TailleHaie {

	private int cadence;
	private boolean etat;

	public int getCadence() { return cadence; }

	public void setCadence(int c) { cadence = c; }

	public void switchOn() { 
		etat = true;
		cadence = 4500;
	}
	
	public void switchOff() {
		etat = false;
		cadence = 0;
	}

	public String toString() {
        return "État : " + (etat?"Allumé":"Éteint") + " Cadence : " + cadence + " coupes/min.";
	}

}

Une procédure de tests est mise en place :

public class TestOutils {
 
  public static void main(String[] args) {
	TailleHaie monTailleHaie = new TailleHaie();
	System.out.println("Taille Haie crée : " + monTailleHaie);
	System.out.println("==> Test init : " + (monTailleHaie.getCadence() == 0) );
	monTailleHaie.switchOn();
	System.out.println("Cadence du Taille Haie en fonctionnement : " + monTailleHaie.getCadence());
	System.out.println("==> Test fonctionnement : " + (monTailleHaie.getCadence() == 4500) );
	monTailleHaie.switchOff();
	System.out.println("Cadence du Taille Haie à l'arret : " + monTailleHaie.getCadence());
	System.out.println("==> Test Arret : " + (monTailleHaie.getCadence() == 0) );
 
	}
 
}

Le résultat est conforme à nos attentes :

Taille Haie crée : État : Éteind Cadence : 0 coupes/min.
==> Test init : true
Cadence du Taille Haie en fonctionnement : 4500
==> Test fonctionnement : true
Cadence du Taille Haie à l'arret : 0
==> Test Arret : true
À noter : il est très important de construire son diagramme de classe de manière rigoureuse, au risque de se retrouver dans l'impossibilité de générer le code Java correspondant. Les méthodes sont à définir de la forme suivante en UML : nom_methode(nom_param : type). Une quelconque autre syntaxe provoquera l'empêchement de la génération du code.

Classe "Tondeuse"

Suivant le même modèle que la classe TailleHaie, nous allons concevoir une classe Tondeuse :

Une tondeuse est caractérisée par la vitesse de rotation de sa lame, typiquement 1000 Tour/minute. On prévoira :
  • switchOn()+: méthode qui allume l’outil et fixe la cadence à 1000.
  • switchOff()+: méthode qui éteint l’outil et fixe la cadence à 0.

La représentation en UML de la classe est également rapide :


On peux constater de grandes similitudes avec la classe TailleHaie, qui se partage donc les méthodes d'allumage et d'extinction, en plus de la cadence de fonctionnement et son état. On pourrais donc créer une classe mère afin de représenter tout les outils de jardinage (voire les outils tout court). À ce moment là nos deux classes Tondeuse et TailleHaie hériteraient de la classe mère Outils, ainsi des méthodes d'allumage et d'extinction et de l'attribut cadence.

Mais pour l'instant, voici le code la classe Tondeuse :

public class Tondeuse {

	private int cadence;
	private boolean etat;

	public Tondeuse() {
		etat = false;
		cadence = 0;
	}
	
	public int getCadence() { return cadence; }

	public void setCadence(int c) { cadence = c; }

	public void switchOn() {
		etat = true;
		cadence = 1000;
	}

	public void switchOff() {
		etat = false;
		cadence = 0;
	}

	public String toString() {
        return "État : " + (etat?"Allumé":"Éteint") + " Cadence : " + cadence + " trs/min.";
	}

}

Testons la avec la procédure de tests :

public class TestOutils {
 
  public static void main(String[] args) {
	Tondeuse maTondeuse = new Tondeuse();
	System.out.println("Tondeuse crée : " + maTondeuse);
	System.out.println("==> Test init : " + (maTondeuse.getCadence() == 0) );
	maTondeuse.switchOn();
	System.out.println("Cadence de Tondeuse en fonctionnement : " + maTondeuse.getCadence());
	System.out.println("==> Test fonctionnement : " + (maTondeuse.getCadence() == 1000) );
	maTondeuse.switchOff();
	System.out.println("Cadence de Tondeuse à l'arret : " + maTondeuse.getCadence());
	System.out.println("==> Test Arret : " + (maTondeuse.getCadence() == 0) );
 
	}
 
}

Le résultat est conforme à nos attentes :

Tondeuse crée : État : Éteint Cadence : 0 trs/min.
==> Test init : true
Cadence de Tondeuse en fonctionnement : 1000
==> Test fonctionnement : true
Cadence de Tondeuse à l'arret : 0
==> Test Arret : true

Mise en facteur et Spécialisation : OutilElectrique

Nous allons donc mettre en facteur les deux classes créées précédemment afin de simplifier les classes. Pour cela, nous allons créer une classe mère OutilElectrique et y définir attributs et méthodes communes aux outils électrique. Pour l'instant il s'agit donc des deux attributs cadence et état, et des méthodes d'allumage et d'extinction.

Voici la classe OutilElectrique modélisé :

La classe OutilElectrique est une classe abstraite.

À noter : une classe abstraite est une classe dont l'implémentation n'est pas complète et qui n'est pas instanciable. Elle sert de base à d'autres classes dérivées (héritées).

Les précédentes classes perdent donc leur attributs et une partie des méthodes, puisqu'elles vont les retrouver en héritant de la classe OutilElectrique. Voici donc la conception mise à jour en conséquence :


Enfin, les codes Java des trois classes, plus allégées :

public class OutilElectrique {

    protected int cadence;
    protected boolean etat;

    public int getCadence() { return this.cadence; }

    public void setCadence(int cadence) { this.cadence = cadence; }

    public void switchOn() { etat = true; }

    public void switchOff() {
        etat = false;
        cadence = 0;
    }

}

public class TailleHaie extends OutilElectrique {

    public TailleHaie() {
        etat = false;
        cadence = 0;
    }

    public void switchOn() {
        super.switchOn();
        cadence = 4500;
    }

    public String toString() {
        return "État : " + (etat?"Allumé":"Éteint") + " Cadence : " + cadence + " coupes/min.";
    }

}

public class Tondeuse extends OutilElectrique {

    public Tondeuse() {
        etat = false;
        cadence = 0;
    }

    public void switchOn() {
        super.switchOn();
        cadence = 1000;
    }

    public String toString() {
        return "État : " + (etat?"Allumé":"Éteint") + " Cadence : " + cadence + " trs/min.";
    }

}

Spécialisation et Enuméré

Nous voici donc devant un détail supplémentaire :

La tondeuse a plusieurs vitesses de traction possibles : arret, lent, moyen ou rapide. Il est possible de changer la vitesse de la tondeuse. Au démarrage, sa vitesse est toujours à moyenne. Lorsque l'on éteint la tondeuse sa vitesse passe à arrêt.

Il nous faut donc modifier le modèle de la classe Tondeuse afin de tenir compte de cette nouvelle information. Afin de définir la vitesse, nous allons déclarer une énumération.


Le code généré est prêt à être utilisé :

public enum Vitesse {
	arret, lent, moyen, rapide
}

Il n'y a plus qu'a utiliser le nouvel attribut vitesse sur les nouvelles classes. En vue d'effectuer des tests plus complet, nous allons effectuer des tests unitaires :

        Tests.Begin("TailleHaie", "1.0.0");

        Tests.Design("Contrôles du constructeur et des accesseurs",4); {

            TailleHaie monTailleHaie = new TailleHaie();

            Tests.Case ("Constructeur"); {

                Tests.Unit("État : Éteint Cadence : 0 coupes/min.", monTailleHaie.toString());

            }

            Tests.Case ("Accesseur de consultation"); {

                Tests.Unit("0", monTailleHaie.getCadence());
                Tests.Unit(Vitesse.arret, monTailleHaie.getVitesse());


                // Mise en route de monTailleHaie
                monTailleHaie.switchOn();

                Tests.Unit("État : Allumé Cadence : 4500 coupes/min.", monTailleHaie.toString());
                Tests.Unit("4500", monTailleHaie.getCadence());
                Tests.Unit(Vitesse.moyen, monTailleHaie.getVitesse());


                // Arrêt de monTailleHaie
                monTailleHaie.switchOff();

                Tests.Unit("État : Éteint Cadence : 0 coupes/min.", monTailleHaie.toString());
                Tests.Unit("0", monTailleHaie.getCadence());
                Tests.Unit(Vitesse.arret, monTailleHaie.getVitesse());



            }

        }

        Tests.End();
        
        Tests.Begin("Tondueuse", "1.0.0");

        Tests.Design("Contrôles du constructeur et des accesseurs",4); {

            Tondeuse maTondeuse = new Tondeuse();

            Tests.Case ("Constructeur"); {

                Tests.Unit("État : Éteint Cadence : 0 trs/min.", maTondeuse.toString());

            }

            Tests.Case ("Accesseur de consultation"); {

                Tests.Unit("0", maTondeuse.getCadence());
                Tests.Unit(Vitesse.arret, maTondeuse.getVitesse());


                // Mise en route de maTondeuse
                maTondeuse.switchOn();

                Tests.Unit("État : Allumé Cadence : 1000 trs/min.", maTondeuse.toString());
                Tests.Unit("100", maTondeuse.getCadence());
                Tests.Unit(Vitesse.moyen, maTondeuse.getVitesse());

                // Arrêt de maTondeuse
                maTondeuse.switchOff();

                Tests.Unit("État : Éteint Cadence : 0 trs/min.", maTondeuse.toString());
                Tests.Unit("0", maTondeuse.getCadence());




            }

        }

        Tests.End();

Le résultat est toujours conforme à nos attentes :

--- Tests unitaires de la classe TailleHaie - Version 1.0.0 ---


** [Contrôles du constructeur et des accesseurs]

+ (Constructeur)

Valeur attendue : État : Éteint Cadence : 0 coupes/min.
Valeur obtenue : État : Éteint Cadence : 0 coupes/min.

+ (Accesseur de consultation)

Valeur attendue : 0
Valeur obtenue : 0

Valeur attendue : arret
Valeur obtenue : arret

Valeur attendue : État : Allumé Cadence : 4500 coupes/min.
Valeur obtenue : État : Allumé Cadence : 4500 coupes/min.

Valeur attendue : 4500
Valeur obtenue : 4500

Valeur attendue : moyen
Valeur obtenue : moyen

Valeur attendue : État : Éteint Cadence : 0 coupes/min.
Valeur obtenue : État : Éteint Cadence : 0 coupes/min.

Valeur attendue : 0
Valeur obtenue : 0

Valeur attendue : arret
Valeur obtenue : arret

=> CR= 2

=> CR= 2

Nombre total de "tests case" : 2
Nombre total de "tests unit" : 2

--- Tests unitaires de la classe Tondueuse - Version 1.0.0 ---


** [Contrôles du constructeur et des accesseurs]

+ (Constructeur)

Valeur attendue : État : Éteint Cadence : 0 trs/min.
Valeur obtenue : État : Éteint Cadence : 0 trs/min.

+ (Accesseur de consultation)

Valeur attendue : 0
Valeur obtenue : 0

Valeur attendue : arret
Valeur obtenue : arret

Valeur attendue : État : Allumé Cadence : 1000 trs/min.
Valeur obtenue : État : Allumé Cadence : 1000 trs/min.

Valeur attendue : 100
Valeur obtenue : 1000

Valeur attendue : moyen
Valeur obtenue : moyen

Valeur attendue : État : Éteint Cadence : 0 trs/min.
Valeur obtenue : État : Éteint Cadence : 0 trs/min.

Valeur attendue : 0
Valeur obtenue : 0

=> CR= 2

=> CR= 2

Nombre total de "tests case" : 2
Nombre total de "tests unit" : 2

Utiliser une classe

Il s'agit de développer à présent un robot “Jardinier”. On peut donner un outil à un jardinier, lui demander de travailler ou d'arrêter de travailler. Voici ses caractéristiques :

  • Nos robots ont tous un nom qui leur est donné à la création. Il n'est pas possible de le modifier par la suite.
  • Si on donne un outil au jardinier alors qu'il en a déjà un, il prend le nouvel outil et relâche l'ancien outil.
  • Si on lui demande de travailler sans lui avoir donné d'outil, il répond par “Donnez-moi un outil!”. S'il a un outil en main, il le démarre et répond par “Je démarre ” suivi de la description de l'outil démarré.
  • Si on lui demande d'arrêter de travailler, il répond par “Merci, la journée a été dure!”. S'il a un outil en main, il l'arrête et le “lâche”.
Passez la souris sur une des caractéristique pour voir une indication concernant la conception.

Ce qui nous donne une classe telle que voici :


Le code généré par Visual Paradigm est une bonne base, mais il faut le compléter :

public class Jardinier {

    private String nom;
    private OutilElectrique outil;

    public Jardinier(String unNom) {
        nom = unNom;
    }

    public void setOutil(OutilElectrique o) { outil = o; }

    public OutilElectrique getOutil() { return outil; }

    public String demandeTravail() {

        if (outil == null) return "Donnez-moi un outil !";
        outil.switchOn();
        return "Je démarre : " + outil.toString();
    }

    public String arretTravail() {
        outil.switchOff();
        outil = null;
        return "Merci, la journée a été dure !";
    }
    
    public String toString() {

        if (outil == null) return "Bonjour, je suis " + nom + ", je n'ai pas d'outil.";
        return "Je suis " + nom + ", je tiens : " + outil.toString();
    }

}

Bien entendu, quelques petits tests :

        System.out.println("=========================TESTS Jardinier =================== ");
        Jardinier monRobot = new Jardinier("R2-D2");
        System.out.println(monRobot.toString());

        System.out.println("Début  du travail pour le jardinier : " + monRobot.demandeTravail());

        Tondeuse maTondeuse = new Tondeuse();
        monRobot.setOutil(maTondeuse);
        System.out.println("On lui a donné la tondeuse : " + monRobot.toString());

        System.out.println("Debut du travail pour le jardinier : " + monRobot.demandeTravail());
        System.out.println("Arret du travail pour le jardinier : " + monRobot.arretTravail());
        System.out.println("La tondeuse doit être à l'arrêt : " + maTondeuse.toString());
        System.out.println("Le jardinier n'a plus d'outil : " + monRobot.toString());

Le résultat est à la hauteur de nos attentes :

=========================TESTS Jardinier ===================
Bonjour, je suis R2-D2, je n'ai pas d'outil.
Début du travail pour le jardinier : Donnez-moi un outil !
On lui a donné la tondeuse : Je suis R2-D2, je tiens : Tondeuse [état=éteint, cadence:0, vitesse:arret]
Debut du travail pour le jardinier : Je démarre : Tondeuse [état=allumé, cadence:1000, vitesse:moyen]
Arret du travail pour le jardinier : Merci, la journée a été dure !
La tondeuse doit être à l'arrêt : Tondeuse [état=éteint, cadence:0, vitesse:arret]
Le jardinier n'a plus d'outil : Bonjour, je suis R2-D2, je n'ai pas d'outil.