====== Classes : modélisation et codage ======
**Objectifs :**
- Faire un lien direct entre la modélisation des classes et les codes correspondants.
- Savoir construire un petit modèle de classes et le mettre en oeuvre.
Il ne reste que 4 séances en tout.
* La semaine du 5 mars est consacrée à cette feuille de TD.
* La semaine du 12 mars est consacrée à des révisions pour l'examen. Vous devez les préparer pour que ces séances soient vraiment efficaces.
===== Classe : Code et modélisation =====
Les notes de bas de page vous donnent des indications sur certaines actions. Il suffit de laisser la souris sur les renvois.
Les temps sont donnés à titre indicatif, ce TD doit aller assez vite, il est simple et vise seulement à bien poser le lien entre conception et développement.
Exercices inspirés de http://www.fresnel.fr/perso/derrode/index.html (le site a disparu... et je n'en trouve plus trace...)
Vous rencontrez beaucoup de soucis avec Modelio. \\
Vous pouvez faire ce TD sur papier mais il vous manquera la génération des codes et le reverse-engineering des codes aux modèles. Vous devez alors voir précisément avec votre enseignant.\\
L'outil VisualParadigm est beaucoup plus stable. Cependant il vous faut la version de test professionnelle pour avoir le générateur de codes.Vous l'aurez au moins jusqu'à la fin du module.
==== Classe ''TailleHaie'' (20mn) ====
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.
- Dessiner la représentation UML de cette classe.
- [[http://mbf-iut.i3s.unice.fr/doku.php?id=modelio#generer_les_codes|Générer]] ou écrivez le code java correspondant à cette classe. On prévoira un constructeur ((Si votre classe s'appelle TailleHaie, le constructeur a pour nom le nom de la classe dans un fichier ''TailleHaie.java'')) qui initialise le tailleHaie dans l’état éteint, avec une cadence de 0((
public class TailleHaie {
private int cadence = 4500;
//Accesseur en lecture
public int getCadence() {
return this.cadence;
}
public void switchOff() {
cadence = 0;
}
public void switchOn() {
cadence = 4500;
}
//Constructeur
public TailleHaie(){
this.setCadence(0);
}
}
)). 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. Vous devez retirer les levées d'exception en implémentant les méthodes.
- On veut pouvoir connaître la cadence du ''TailleHaie''.
- Tester votre code. Si vous n'avez pas encore vu la notion de Test en java vous pouvez mettre vos tests dans une classe dédiée comportant un ''main'' et votre test peut ressembler à ce qui suit.
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) );
}
}
Sinon utilisez de préférence des tests unitaires, comme dans l'exemple di-dessous.
import org.junit.Before;
import org.junit.Test;
public class TailleHaieTest {
TailleHaie trimmer;
@Before
public void setUp() throws Exception {
trimmer = new TailleHaie();
}
@Test
public void init() throws Exception {
assertEquals("Taille haie a une cadence initiale de 0",0, trimmer.getCadence());
}
@Test
public void switchOn() throws Exception {
trimmer.switchOn();
assertEquals("Taille haie a une cadence de 4500",4500, trimmer.getCadence());
}
@Test
public void switchOff() throws Exception {
trimmer.switchOn();
trimmer.switchOff();
assertEquals("Taille haie éteinte",0, trimmer.getCadence());
}
}
Maintenez le modèle et le code en cohérence((si outil : Code -> Java Round-trip)) .
==== Classe ''Tondeuse'' (10mn) ====
Sur le même modèle, une tondeuse est caractérisée par
* la vitesse de rotation de sa lame, typiquement 1000 Tour/minute;
* une cadence;
* switchOn()+: méthode qui allume l’outil et fixe la cadence à 1000;
* switchOff()+: méthode qui éteint l’outil et fixe la cadence à 0.
- Dessiner la représentation UML de cette classe et continuez comme précédemment.
- Que constatez-vous? Quels sont les éléments communs? Ne pourrait-on pas "partager" des informations?
- Ecrivez vos tests dans la même classe que précédemment et attention ils doivent **tous** continuer à fonctionner. Vous pouvez par exemple ajouter les lignes suivantes.
System.out.println("=========================TESTS TONDEUSE===================");
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) );
==== Mise en facteur et Spécialisation : OutilElectrique (10mn) ====
- Mettez en facteur les éléments qui peuvent l'être dans la modélisation UML en ajoutant une classe ''OutilElectrique''.
- Pour les plus avancés, en fonction de vos choix, déterminez s'il s'agit d'une classe abstraite ou non. ((Un attribut privé n'est pas accessible par les sous-classes, vous devez donc utiliser les accesseurs.))
- Modifier la modélisation et les codes en conséquence.((
public abstract class OutilElectrique {
private int cadence = 4500;
public int getCadence() {
return this.cadence;
}
protected void setCadence(int cadence){
this.cadence = cadence;
}
public OutilElectrique() {
this.cadence = 0;
}
public boolean isOn(){
return cadence != 0;
}
public void switchOff() {
cadence = 0;
}
public abstract void switchOn();
@Override
public String toString() {
return "OutilElectrique [cadence=" + cadence + "]";
}
}
Pour la classe qui étend :
public class TailleHaie extends OutilElectrique{
public void switchOn() {
this.setCadence(4500);
}
//Constructeur
public TailleHaie(){
this.setCadence(0);
}
}
))
- Réécrire en conséquence les classes ''TailleHaie'' et ''Tondeuse''.
- Relancer vos tests **sans les modifier**! Ils doivent toujours fonctionner!
/* ----------------
*/
==== (Facultatif) Spécialisation et Enuméré (10 mn) ====
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.
- Modifier le **modèle** de la classe Tondeuse pour tenir compte de cette nouvelle information. Les Vitesses sont un //énuméré//. Créer une //enumeration// dans le modèle, lui ajouter les ''enumerate literal'' //arret//, ...
- Générer vos codes.
- Compléter vos codes pour gérer la vitesse. Pour accéder dans votre code par exemple à l'énuméré "arret", vous écrirez par exemple, ''v = Vitesse.arret;''
- **Etendez** les tests précédents pour vérifier que vos codes font bien ce qui est attendu. Vous devez lancer TOUS les tests. Vous constatez à ce stade que la vérification de la réussite ou non des tests est plus "longue", c'est pourquoi aujourd'hui on utilise des méthodes de tests qui systématise les vérifications et signalent spécifiquement les erreurs.
System.out.println("=========================TESTS TONDEUSE ETENDU ===================");
//La tondeuse a plusieurs vitesses de traction possibles : arret, lent, moyenne ou rapide.
maTondeuse.switchOff();
System.out.println("Vitesse de Tondeuse à l'arret attendue "+ Vitesse.arret +"==>" + maTondeuse.getVitesse());
//Il est possible de changer la vitesse de la tondeuse.
maTondeuse.switchOn();
System.out.println("Vitesse de Tondeuse au démarrage attendue "+ Vitesse.moyen +"==>" + maTondeuse.getVitesse());
maTondeuse.setVitesse(Vitesse.rapide);
System.out.println("Vitesse de Tondeuse attendue "+ Vitesse.rapide +"==>" + maTondeuse.getVitesse());
//Au démarrage, sa vitesse est toujours à l'arrêt. Lorsque l'on éteint la tondeuse sa vitesse passe à arrêt.
maTondeuse.switchOff();
System.out.println("Vitesse de Tondeuse à l'arret attendue "+ Vitesse.arret +"==>" + maTondeuse.getVitesse());
maTondeuse.setVitesse(Vitesse.rapide);
maTondeuse.switchOn();
System.out.println("Vitesse de Tondeuse au démarrage attendue (alors que on a modifie sa vitesse "+ Vitesse.moyen +"==>" + maTondeuse.getVitesse());
maTondeuse.switchOff();
N'hésitez pas à ajouter des commentaires dans vos codes, ils sont conservés par le java round-trip!
==== Utiliser une classe (25mn) ====
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.
* 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".
- Donner une représentation UML du problème du ''Jardinier''.
- Générer les codes correspondants. Si les résultas ne vous satisfont pas, corriger votre modèle. En particulier, vous devrez utiliser la navigation entre les classes.
- Compléter les codes correspondants.
- Eventuellement par reverse-engineering, reconstruisez votre modèle.
- Evidemment tester votre programme. Voici un exemple de trace possible à l'exécution des tests.
=========================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 [vitesse=arret, cadence=0]
Debut du travail pour le jardinier : Je démarre : Tondeuse [vitesse=moyen, cadence=1000]
Arret du travail pour le jardinier : Merci, la journée a été dure!
La tondeuse doit être à l'arrêt : Tondeuse [vitesse=arret, cadence=0]
Le jardinier n'a plus d'outil : Je suis R2-D2 : je n'ai pas d'outil
- Et si nous voulions représenter plusieurs sortes de Robot, que ferions-nous? Et si tout robot était un Outil Electrique ?
Pour vous aider :
* Vous ne pouvez pas modifier le nom du Jardinier donc vous devez lui donner un nom à la création, pour cela vous créez un constructeur :
public class Jardinier{
// Un constructeur qui prend le nom en parametre
public Jardinier(String nom) {
this.nom = nom;
}
* Pour créer un jardinier maintenant, vous devez lui donner un nom.
Jardinier monJardinier = new Jardinier("R2-D2");
* Pour visualiser un objet java, vous pouvez redéfinir son affichage en implémenentant une méthode ''String ToString()'' par exemple :
public String toString() {
String s = "Je suis " + nom ;
if (outil== null)
s += " : je n'ai pas d'outil ";
else
s += ", je tiens : " + outil ;
return s;
}
===== Allons plus loin =====
Cet exercice doit être réalisé autant que possible, **seul**.
Vous devez réussir à présent à analyser puis coder un "ptit" problème comme celui-ci.
A présent notre robot peut utiliser un véhicule.
==== Déplacement d'un véhicule (15mn) ====
Un ''véhicule'' a une ''position'' (X, Y en entier).
Il peut se déplacer sur l'axe des x ou des y.
Il peut être initialisé, ce qui le place en position (0,0).
Un ''charriot'' est un véhicule qui se déplace d'un delta de 10 à chaque demande de déplacement : s'il est en (0,0), déplacer le chariot sur X l'amène en (10,0), puis en déplacement sur XY l'amène en (20,10).
Une ''fusee'' est un véhicule qui se déplace d'un delta de 100 à chaque demande de déplacement.
- Définissez les classes et les codes associés.
- Si à présent nous voulons modifier une position de delta sur l'axe des X, des Y, en diagonale, que faîtes-vous?
- Si nous voulons à présent que notre véhicule soit repéré par des coordonnées GPS ou polaires au lieu de coordonnées cartésiennes... Que devez_vous modifier?
===================TESTS Vehicules ===================
Chariot => Vehicule [position=[0,0]]
Avance sur X, position attendue [10,0] : true
Avance sur Y, position attendue [10,10]: true
Fusee => Vehicule [position=[0,0]]
Avance sur position attendue [100,0] : true
Avance sur position attendue [100,100] : true
==== Le robot conduit le véhicule (20mn) ====
On peut donner un véhicule au robot. Il l'initialise quand il le reçoit.
On peut demander au robot de se déplacer selon un schéma donné : "XYXX"((la méthode toCharArray() appliquée sur une String renvoie un tableau de "Char")).
Le robot déplacera alors le véhicule de X puis de Y puis de X puis de X.
Quand le robot arrête de travailler il ramène le véhicule en position initiale.
Voici un exemple de trace obtenue en testant ce qui est demandé.
===================TESTS jardinier & Vehicules ===================
Pas de vehicule : Je suis R2-D2 : je n'ai pas d'outil je n'ai pas de vehicule
Avec un charriot : Je suis R2-D2 : je n'ai pas d'outil , j'ai ce vehicule Chariot => Vehicule [position=[0,0]]
Deplacement du jardinier en XYX, on attend (0,0),(10,0) (10,10) (20,10)
Nouvelle position attendue [20,10] : true
Avec une fusee : Je suis R2-D2 : je n'ai pas d'outil , j'ai ce vehicule Fusee => Vehicule [position=[0,0]]
Deplacement du jardinier en XYX, on attend (0,0),(100,0) (100,100) (200,100)
Nouvelle position attendue [200,100] : true
==== D'un diagramme de séquence au code (10 mn) ====
{{:2016_2017:s2:td:diagseq2code_2017-02-12_a_21.36.44.png?300|}}
Définir un code qui correspond au début de scénario ci-dessus.
===== Un robot facteur =====
Nous sommes très content de notre robot jardinier.
Nous décidons de construire un robot facteur.
Il transporte des courriers.
Suivez l'énoncé pas à pas. Il est écrit ainsi pour vous aider.
Des exemples de tests sont donnés pour vous aider, en particulier à définir les méthodes associées à vos classes.
==== Courrier ====
Un courrier est défini par une adresse et un contenu.
Pour des raisons de confidentialité, une fois créé, si on demande l'adresse ou le contenu du courrier, on obtient un texte illisible : il est crypté en utilisant un encrypteur donné plus bas et une clef donnée à la construction du courrier.
Voici le modèle correspondant à la classe "Encryptor"
{{ :2015_2016:s2:td:capture_d_e_cran_2016-02-20_a_14.22.00.png?direct&200 |}}
**Le courrier ne contient pas le texte initial, ni la clef de cryptage. Elles sont seulement connues à la création du courrier.** On ne peut pas modifier l'adresse ou le contenu d'un courrier.
Exemple de tests et de trace :
String adresseOrigine = "Avenue Fabron, Nice";
Courrier mail = new Courrier(adresseOrigine,"Bravo ", 3);
System.out.println("Courrier adresse illisible " + mail.getAdresse());
System.out.println("Courrier contenu illisible " + mail.getContenu());
===================TESTS Courrier ===================
Courrier adresse illisible rEV]FVuRQA\]}ZPV
Courrier contenu illisible qARE\
**QUESTIONS**
- Commencer par créer la classe ''Encriptor'' en recopiant "simplement" le code donné plus bas.
- Regarder le code de tests de courrier :
- A quoi correspond d'après vous le 3 ?
- Pourquoi la réponse à ''getAdresse'' est-ce un texte crypté?
- Dans l'ordre que vous voulez, répondez à ces deux questions, et tester votre code.
- Quel est le modèle de la classe ''Courrier''?
- Quel est son code ?
==== Decrypteur ====
Pour lire le courrier il faut utiliser un décrypteur, c'est un outil électrique.
Quand il est allumé, si on lui présente un courrier, et on lui donne la clef, il nous donne l'adresse en claire, sinon il renvoie l'adresse telle que.
Exemple de tests et de trace :
Decryptor decryptor = new Decryptor();
System.out.println("pas de lecture si non demarre " + decryptor.readAddress(mail,3));
decryptor.switchOn();
System.out.println("lecture efficiente " + decryptor.readAddress(mail,3) + " : " + decryptor.readAddress(mail,3).equals(adresseOrigine) );
===================TESTS Decrypteur ===================
pas de lecture si non demarre rEV]FVuRQA\]}ZPV
lecture efficiente : Avenue Fabron, Nice : true
**QUESTIONS**
- Quelles sont les méthodes de la classe ''Decryptor'' ? Quelle est la relation entre ''Decryptor'' et ''OutilElectrique'' ?
- La classe ''Decryptor''a besoin de créer un ''Encyptor'' à chaque appel en lecture en lui passant la bonne clef.
- Implémenter la classe ''Decryptor''.
==== Facteur ====
Notre robot facteur peut avoir un véhicule pour se déplacer et un décrypteur pour lire le courrier. Quand on lui remet un courrier, on lui donne la clef pour lire le courrier.
Exemple de tests et de trace simple:
System.out.println("===================TESTS Facteur ===================");
Facteur facteur = new Facteur("Hermes");
//Une distribution simple
facteur.distribue(mail,3);
String adresse = facteur.lire();
System.out.println("Je dois aller à " + adresse);
facteur.setVehicule(charriot);
facteur.deplacer("XXYYY");
Courrier courrierDepose = facteur.depose();
System.out.println("Courrier" + courrierDepose +" deposé à l'adresse : " + adresse );
System.out.println("Je suis en [20,30]: " + charriot.getPosition());
Je dois aller à Avenue Fabron, Nice
CourrieroutilsPK.Courrier@511d50c0 deposé à l'adresse : Avenue Fabron, Nice
Je suis en [20,30]: [20,30]
Je dois aller à Avenue Fabron, Nice
Courrier deposé à l'adresse : Avenue Fabron, Nice
Je suis en [20,30]: [20,30]
**QUESTIONS**
- Quel est le modèle de la classe ''Facteur''?
- Quelles sont les méthodes que doit avoir un Facteur?
- Qu'ont en commun un ''Facteur'' et un ''Jardinier''?
- Implémenter la classe ''Facteur''
- Le facteur a-t-il bien lâché le courrier une fois déposé ? Que faîtes-vous si vous demandez au facteur de lire le courrier alors qu'il n'en a plus ? Par exemple :
adresse = facteur.lire();
System.out.println("Je dois aller à " + adresse);
Je dois aller à Rentre chez toi
==== Facteur encore ====
Evidemment notre facteur ne distribue pas un seul courrier.
Exemple de tests et de trace :
System.out.println("===================TESTS Facteur ===================");
//3 courriers sont remis à notre facteur avec pour chacun la clef pour lire leur adresse.
Courrier c2 = new Courrier("Petit Prince Planete","Je veux etre ton ami ",2);
Courrier c3 = new Courrier("Pere Noel","Je voudrais un robot voyageur ",5);
Facteur facteur = new Facteur("Hermes");
facteur.distribue(mail,3);
facteur.distribue(c2,2);
facteur.distribue(c3,2);
String adresse = facteur.lire();
System.out.println("Je dois aller à " + adresse);
facteur.setVehicule(charriot);
facteur.deplacer("XXYYY");
Courrier courrier = facteur.depose();
System.out.println("Courrier deposé à l'adresse : " + adresse );
System.out.println("Je suis en [20,30]: " + charriot.getPosition());
adresse = facteur.lire();
System.out.println("Je dois aller à :" + adresse);
System.out.println("Je prends ma fusee");
facteur.setVehicule(fusee);
facteur.deplacer("XYXYXYXYXYXYXY");
courrier = facteur.depose();
System.out.println("Courrier depose à l'adresse " + adresse);
System.out.println("Je suis en [700,700] : " + fusee.getPosition());
adresse = facteur.lire();
System.out.println("Je dois aller à : " + adresse);
System.out.println("Je ne comprends pas, même pas capable de me donner la bonne clef!");
facteur.depose();
System.out.println("Courrier perdu ");
System.out.println("Je suis toujours en [700,700] : " + fusee.getPosition());
adresse = facteur.lire();
System.out.println("J'ai fini : " + adresse);
===================TESTS Facteur ===================
Je dois aller à Avenue Fabron, Nice
Courrier deposé à l'adresse : Avenue Fabron, Nice
Je suis en [20,30]: [20,30]
Je dois aller à : Petit Prince Planete
Je prends ma fusee
Courrier depose à l'adresse Petit Prince Planete
Je suis en [700,700] : [700,700]
Je dois aller à :Wbub'Ihbk
Je ne comprends pas, même pas capable de me donner la bonne clef!
Courrier perdu
Je suis toujours en [700,700] : [700,700]
J'ai fini : Rentre chez toi
**QUESTIONS**
- Que devez-vous modifier pour que le ''Facteur'' remplisse bien son rôle ?
- Améliorer la classe ''Facteur''
==== CODES ====
=== Encrypteur ===
Voici le code pour crypter il est basé sur : [[http://blog.idleman.fr/snippet-14-java-crypter-et-decrypter-une-chaine-de-caractere/|un code]] qui ne prend pas de clef, ici à la construction de l'"Encrypteur" on lui passe une clef qui est évidemment secrète.
public class Encryptor {
private final int key;
public Encryptor(int key) {
super();
this.key = key;
}
public String encrypt(String password){
String crypte="";
for (int i=0; i
Exemple de tests de l'encryptor :
Encryptor d = new Encryptor(2);
String origine = "Avenue Fabron, Nice";
String sCryptee = d.encrypt(origine);
System.out.println("Pas lisible " + sCryptee);
String r = d.decrypt(sCryptee);
System.out.println(r + " : " + r.equals(origine));
/*
===== Reverse Engineering et diagramme de séquence =====
{{ :2015_2016:s2:td:capture_d_e_cran_2016-02-27_a_13.21.06.png?direct&200 |}}
Dans le TD précédent, vous avez implémenté le déplacement du Robot qui déplace son véhicule.
- Visualisez cette méthode sous la forme d'un diagramme de séquence.
- Repérez la correspondance entre votre code, les lignes de vie et les envois de message.
- Qui implémente le message? Qui déclenche l'envoi de message?
- Faîtes la même chose pour votre programme de tests.
*/