====== Corrections : modélisation et codage ====== ===== Classe : Code et modélisation ===== ==== Classe ''TailleHaie'' ==== Un exemple initial de code généré à partir du diagramme ci-après et complété par quelques lignes de code. package outilsPK; public class TailleHaie { private int _cadence = 4500; public void switchOn() { _cadence = 4500; } public void switchOff() { _cadence = 0; } //Ajoutée pour accéder à la cadence public int get_cadence() { return _cadence; } //non demandée et ne doit pas être accessible private void set_cadence(int cadence) { this._cadence = cadence; } public TailleHaie(){ _cadence = 0; } } {{ :2013_2014:s2:td:corrections:taillehaie.png?direct&200 |}} ==== Classe ''Tondeuse'' ==== package outilsPK; public class Tondeuse { private int _cadence = 4500; public void switchOn() { _cadence = 1000; } public void switchOff() { _cadence = 0; } /** * non demandée et ne doit pas être accessible */ private void set_cadence(int aCadence) { throw new UnsupportedOperationException(); } public Tondeuse() { throw new UnsupportedOperationException(); } public int getCadence() { return this._cadence; } } On remarque alors que l'attribut (variable d'instance) ''cadence'' et la méthode ''switchOff'' sont les mêmes dans les deux classes. ==== Mise en facteur : OutilElectrique ==== Voici le code et le modèle en concordance. Il est possible de ne remonter que des méthodes abstraites si vous ne connaissez pas "protected". {{ :2013_2014:s2:td:corrections:apres_refactoring.png?nolink&200 |}} package outilsPK; public abstract class OutilElectrique { private int cadence = 4500; public int get_cadence() { return this.cadence; } public void switchOff() { cadence = 0; } public abstract void switchOn(); protected void setCadence(int cadence){ this.cadence = cadence; } } package outilsPK; public class TailleHaie extends OutilElectrique{ public void switchOn() { this.setCadence(4500); } public TailleHaie(){ this.setCadence(0); } } package outilsPK; public class Tondeuse extends OutilElectrique { public void switchOn() { setCadence(1000); } public Tondeuse() { setCadence(0); } } ==== Tester ==== package outilsPK; import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; public class OutilElectriqueTest { private TailleHaie th; private Tondeuse td; @Before public void setUp() throws Exception { th = new TailleHaie(); td = new Tondeuse(); } @Test public void testSwitchOnTailleHaie() { th.switchOn(); assertEquals(4500, th.getCadence()); } @Test public void testSwitchOnTondeuse() { td.switchOn(); assertEquals(1000, td.getCadence()); } @Test public void testSwitchOff() { th.switchOff();td.switchOff(); assertEquals(0, th.getCadence()); assertEquals(0, td.getCadence()); } @Test public void testSwitchOff_On_Off() { testSwitchOff(); testSwitchOnTondeuse(); testSwitchOnTailleHaie(); testSwitchOff(); } } ==== Utiliser une classe ==== Diagramme UML avant génération du code {{ :2013_2014:s2:td:corrections:jardinier.png?nolink&200 |}} Puis on génére et compléte la classe ''jardinier'' avec son test package outilsPK; public class Jardinier { protected OutilElectrique getOutil() { return outil; } protected String getPrenom() { return prenom; } private OutilElectrique outil; private String prenom; /** * * @param outil */ public void startTravail(OutilElectrique outil) { if (this.outil != null) this.outil.switchOff(); this.outil = outil; outil.switchOn(); } public void stopTravail() { outil.switchOff(); outil = null; } @Override public String toString() { return "Jardinier [outil=" + outil + ", prenom=" + prenom + "]"; } public Jardinier(String prenom) { this.prenom = prenom; } } package outilsPK; import static org.junit.Assert.*; package outilsPK; import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; public class JardinierTest { private TailleHaie th; private Tondeuse td; private Jardinier j; @Before public void setUp() throws Exception { th = new TailleHaie(); td = new Tondeuse(); j = new Jardinier("Gaston"); } //A revoir car trop de tests en un. @Test public void testJardinerTravaille() { assertEquals(null, j.getOutil()); j.startTravail(th); assertEquals(th, j.getOutil()); // le tailleHaie a été démarré assertEquals(4500, th.getCadence()); j.startTravail(td); // On a bien éteint le tailleHaie assertEquals(0, th.getCadence()); assertEquals(td, j.getOutil()); j.stopTravail(); assertEquals(null, j.getOutil()); assertEquals(0, td.getCadence()); } @Test public void testPrenom() { assertEquals("Gaston", j.getPrenom()); } } Revoici le diagramme UML après reverse... {{ :2013_2014:s2:td:corrections:jardinierv2.png?nolink&200 |}} ==== Gérer la vitesse : spécialisation de classes et enuméré ==== On ajoute la vitesse comme un enumerate dans le modèle et les méthodes demandées puis on met à jour les codes. package outilsPK; public enum Vitesse { arret, lent, moyen, rapide } package outilsPK; public class Tondeuse extends OutilElectrique { private Vitesse vitesse ; protected Vitesse getVitesse() { return vitesse; } public void switchOn() { setCadence(1000); } public Tondeuse() { setCadence(0); vitesse = Vitesse.arret; } public void setVitesse(Vitesse v){ vitesse = v; } public void switchOff() { super.switchOff(); vitesse = Vitesse.arret; } } package outilsPK; import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; public class OutilElectriqueTest { private TailleHaie th; private Tondeuse td; @Before public void setUp() throws Exception { th = new TailleHaie(); td = new Tondeuse(); } @Test public void testSwitchOnTailleHaie() { th.switchOn(); assertEquals(4500, th.getCadence()); } @Test public void testSwitchOnTondeuse() { td.switchOn(); assertEquals(1000, td.getCadence()); } @Test public void testVitesseTondeuse() { td.switchOn(); td.setVitesse(Vitesse.moyen); assertEquals(Vitesse.moyen, td.getVitesse()); td.switchOff(); assertEquals(Vitesse.arret, td.getVitesse()); } @Test public void testSwitchOff() { th.switchOff();td.switchOff(); assertEquals(0, th.getCadence()); assertEquals(0, td.getCadence()); } @Test public void testSwitchOff_On_Off() { testSwitchOff(); testSwitchOnTondeuse(); testSwitchOnTailleHaie(); testSwitchOff(); } } ===== Reverse et agrégation ===== ==== Reverse : La classe ''Point'' ==== {{ :2013_2014:s2:td:corrections:polygonev0.png?nolink&400 |}} ==== La classe Chemin ==== package trajetPK; import java.awt.*; public class Chemin { Point depart; Point arrivee; /** * * @param depart * @param arrivee */ public Chemin(Point depart, Point arrivee) { this.depart = depart; this.arrivee = arrivee; } public double distance() { int y = arrivee.y - depart.y; int x = arrivee.x - depart.x; return Math.sqrt( y*y + x*x); } @Override public String toString() { return "Chemin [depart=" + depart + ", arrivee=" + arrivee + "]"; } } package trajetPK; import static org.junit.Assert.*; import java.awt.Point; import org.junit.Before; import org.junit.Test; public class CheminTest { private static final double DELTA = 1e-15; Chemin c ; @Before public void setUp() throws Exception { c = new Chemin(new Point(-7,-2), new Point(5,3)); } @Test public void testDistance() { System.out.println(c.distance()); assertEquals(13.0,c.distance(),DELTA); } } ==== La classe Trajet ==== package trajetPK; public class MauvaisTrajetException extends Exception { } package trajetPK; import java.util.*; public class Trajet { ArrayList chemins; public Trajet(ArrayList chemins) throws MauvaisTrajetException { super(); this.chemins = chemins; Iterator it = chemins.iterator(); if (!(it.hasNext())) throw new MauvaisTrajetException(); Chemin courant = it.next(); Chemin suivant; while (it.hasNext()) { suivant = it.next(); if (courant.arrivee.equals(suivant.depart)) courant = suivant; else throw new MauvaisTrajetException(); } } @Override public String toString() { return "Trajet [chemins=" + chemins + "]"; } } package trajetPK; import static org.junit.Assert.*; import java.awt.Point; import java.util.ArrayList; import java.util.Arrays; import org.junit.Before; import org.junit.Test; public class TrajetTest { private static final double DELTA = 1e-15; Trajet trValide ; Trajet trUn ; Trajet trInvalide ; Chemin c1 ; Chemin c2 ; Chemin c3; @Before public void setUp() throws Exception { c1 = new Chemin(new Point(-7,-2), new Point(5,3) ); c2 = new Chemin(new Point(5,3), new Point(7,5) ); c3 = new Chemin(new Point(7,5) , new Point(9,11) ); } @Test public void testCreerValide() throws MauvaisTrajetException { trValide = new Trajet ( new ArrayList(Arrays.asList(new Chemin[]{ c1, c2, c3 }))); System.out.println(trValide); } @Test public void testUn() throws MauvaisTrajetException { trUn = new Trajet ( new ArrayList(Arrays.asList(new Chemin[]{ c1}))); } @Test(expected = MauvaisTrajetException.class) public void testInvalide() throws MauvaisTrajetException { trInvalide = new Trajet ( new ArrayList(Arrays.asList(new Chemin[]{ c1, c3 }))); } {{ :2013_2014:s2:td:corrections:trajet.png?nolink&500 |}} ==== La classe ''Polygone'' V1 et V2 ==== Un polygone est composé d'un ensemble de points. J'ai utilisé de suite une liste de points qui permet de supporter le addPoint. Mais si j'avais utilisé un tableau, alors j'aurais modifié ma classe dans l'exercice suivant pour supporter le addPoint. C'est trop tard ;-) package geometrie; import java.util.ArrayList; import java.util.Arrays; import java.awt.Point; public class Polygone { public ArrayList _points = new ArrayList(); public Polygone() { } public Polygone(Point[] points) { _points = new ArrayList(Arrays.asList(points)); } @Override public String toString() { String pointsAsString =" "; for(Point p : _points) pointsAsString += p + " ; "; return "Polygone [_points=" + pointsAsString + "]"; } /** * * @param p */ public void addPoint(Point p) { _points.add(p); } } ==== La classe ''Polygone'': V2 ==== * Pour remplir le tableau de points, on a besoin d'une première méthode ''addPoint'' qui ajoutera un point à ceux déjà présents. - Etendez votre modélisation de la classe ''Polygone'' en UML. - Générer le code. Quelle structure de données utilisez vous? Regarder le code généré ... - Tester votre classe. - Que retenez-vous de cet exercice? Quand se fait le choix de la structure?