diff --git a/.gitignore b/.gitignore index d855be8950d9171b07c2f6f0b29580ea277906ef..dc4040c77db802a7a7343bbf5746899684aebfb6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,35 @@ bin/ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ +### IntelliJ IDEA ### +.idea/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### diff --git a/README.md b/README.md index afb1d7fdaaa46029ab801860ee0731ebd87283f9..a6d91a3c654c8a9e12a4f64377fec7c7c7aec0be 100644 --- a/README.md +++ b/README.md @@ -1,115 +1,58 @@ -# TP4 - Observateur/Observé +# TP3 - Observateurs/Observable -Dans ce TP, vous allez vous familiariser avec les notions observateur/observé -notamment en implémentant les classes nécessaires puis en les utilisant pour -réaliser une horloge simple. +Dans ce TP, vous allez vous familiariser avec le patron de conception observateurs/observable. +D'abord, en implémentant sa structure et son mécanisme. +Puis, en utilisant cette couche abstraite pour réaliser un petit programme présentant des objectifs sur des saisies utilisateurs. -## Le patron Observateur / Observé +## Le patron Observateurs / Observable -La figure suivante vous présente le patron Observateur (ici « Observer ») / Observé (ici « Subject »). -L’idée est qu’un observateur peut s’inscrire auprès d’un Observé et être avertis d’évènements qui se produisent sur l’Observé. -La méthode « update() » des Observateurs est déclenchée par l’Observé chaque fois qu’il veut prévenir d’un évènement. +La figure suivante vous présente le patron Observateurs (ici « Observer ») / Observable. +L’idée est qu’un ou plusieurs observateurs peuvent s’inscrire auprès d’un Observable et être avertis d’évènements qui se produisent sur ce dernier. +La méthode « update() » des Observateurs est déclenchée par l’Observable chaque fois qu’il veut prévenir d’un évènement. Il utilise pour ça sa propre méthode « notifyObservers() ». ```mermaid classDiagram direction LR - class Subject - <<Abstract>> Subject - Subject --> "*" Observer : #observers - Subject ..> Observer : notifies - Subject : +attach(Observer) void - Subject : +detach(Observer) void - Subject : #notifyObservers() void - Subject : #notifyObservers(Object) void + class Observable + <<Abstract>> Observable + Observable --> "*" Observer : #observers + Observable ..> Observer : notifies + Observable : +attach(Observer) void + Observable : +detach(Observer) void + Observable : #notifyObservers() void + Observable : #notifyObservers(Object) void class Observer <<interface>> Observer - Observer : +update(Subject) void - Observer : +update(Subject, Object) void + Observer : +update(Observable) void + Observer : +update(Observable, Object) void ``` -**Q1.1** Compléter la classe abstraite Subject de manière à pouvoir disposer du mécanisme Observateur/Observé. +**Q1.1** Compléter la classe abstraite Observable de manière à pouvoir disposer du mécanisme Observateur/Observable. Vérifiez la validité de votre implémentation à l'aide des tests fournis. ```java package fr.univlille.iut.r304.tp4.q1; -public abstract class Subject { - public void attach(Observer obs) - public void detach(Observer obs) - protected void notifyObservers() - protected void notifyObservers(Object data) +public abstract class Observable { + public void attach(Observer obs){} + public void detach(Observer obs){} + protected void notifyObservers(){} + protected void notifyObservers(Object data){} } ``` -Nous utiliserons ce pattern pour implémenter un (des) chronomètre(s). +Nous utiliserons ce pattern pour implémenter un (des) analyseur(s) de textes saisis. -On vous fournit la classe suivante (*TimerThread*) qui implémente un thread simple qui produit un évènement toutes les secondes. -Ce TimerThread permettra de déclencher la mise à jour des horloges qui afficheront l’heure. - -```java -public class TimerThread extends Thread { - @Override - public void run(){ - while (true) { - try { - sleep(1000); - // annoncer le « tick-horloge » - } catch (InterruptedException e) { - // on ignore et on espère que ce n’est pas grave - } - } - } -} -``` - -Le thread s’utilise de cette façon : `new TimerThread().start();` (ce qui provoque l'appel à la méthode `void run()`) et boucle sans fin -en faisant une action (que **vous** devez définir) chaque seconde. - -On souhaite instancier deux chronomètres, qui se mettent à jour toutes les secondes. -Ces deux chronomètres utiliseront le même Timer mais auront des temps écoulés différents (par simplicité, vous pouvez juste les initialiser à des valeurs différentes. +On souhaite instancier deux règles, qui se mettent à jour à chaque ligne saisie. +Ces deux règles utiliseront les mêmes saisies mais vérifieront des critères différents. -```mermaid -classDiagram - direction LR - - class Subject - <<Abstract>> Subject - Subject --> "*" Observer : #observers - Subject : +attach(Observer) void - Subject : +detach(Observer) void - Subject : #notifyObservers() void - Subject : #notifyObservers(Object) void - - class Observer - <<interface>> Observer - Observer : +update(Subject) void - Observer : +update(Subject, Object) void - - Timer --|> Subject - Chronometer ..|> Observer -``` - - -**Q1.2** Pour l’implémentation de Timer, on s’aperçoit d’un problème d’héritage. -Quelles sont les deux classes dont ce Timer devrait hériter ? - -**Q1.3** Proposez une solution pour ce problème (c-a-d implémentez le Timer comme un Subject observable). -Implémentez aussi une classe chronomètre qui puisse être notifié par le Timer toutes les secondes et affiche le nombre de secondes écoulées. - -```java -package fr.univlille.iut.r304.tp4.q1; -public class Timer - public void start() - public void stopRunning() - ... -``` Note: Des tests sont fournis dans le projet pour que vous vous assuriez que vos classes fonctionnent comme attendu. Respectez les noms des packages, classes et méthodes -**Q1.4** +**Q1.2** Lors des épreuves de course sportive, il n'est pas rare d'afficher en parallèle le chronomètre de l'étape et celui du temps total. Dans cette idée, écrivez un main avec deux chronomètres console qui affichent simplement leur nom et le nombre de secondes écoulées en boucle, l'un démarrant à zéro, l'autre à une valeur saisie au départ. On doit par exemple pouvoir dérouler le scénario suivant : @@ -168,9 +111,9 @@ Vérifier la validité de votre implémentation en exécutant les tests associé package fr.univlille.iut.r304.tp4.q2; public class ConnectableProperty extends ObservableProperty - public void connectTo(ConnectableProperty other) - public void biconnectTo(ConnectableProperty other) - public void unconnectFrom(ConnectableProperty other) + public void connectTo(ConnectableProperty other){} + public void biconnectTo(ConnectableProperty other){} + public void unconnectFrom(ConnectableProperty other){} ... ``` diff --git a/lib/junit-platform-console-standalone-1.10.0-M1.jar b/lib/junit-platform-console-standalone-1.10.0-M1.jar new file mode 100644 index 0000000000000000000000000000000000000000..a2868c8af7975e22dfe46b58444b36def984da6f Binary files /dev/null and b/lib/junit-platform-console-standalone-1.10.0-M1.jar differ diff --git a/out/production/main/fr/univlille/iut/r304/tp4/q1/Observable.class b/out/production/main/fr/univlille/iut/r304/tp4/q1/Observable.class new file mode 100644 index 0000000000000000000000000000000000000000..1c4244fded8eba21f7ee2333939fad0bcc10e3d7 Binary files /dev/null and b/out/production/main/fr/univlille/iut/r304/tp4/q1/Observable.class differ diff --git a/out/production/main/fr/univlille/iut/r304/tp4/q1/Observer.class b/out/production/main/fr/univlille/iut/r304/tp4/q1/Observer.class new file mode 100644 index 0000000000000000000000000000000000000000..bdbc554d07a2311a0819cfaf6d9318e35efccdad Binary files /dev/null and b/out/production/main/fr/univlille/iut/r304/tp4/q1/Observer.class differ diff --git a/out/production/main/fr/univlille/iut/r304/tp4/q2/ConnectableProperty.class b/out/production/main/fr/univlille/iut/r304/tp4/q2/ConnectableProperty.class new file mode 100644 index 0000000000000000000000000000000000000000..ad0999eea2d7db11370cd2eb338c9eea4e64370b Binary files /dev/null and b/out/production/main/fr/univlille/iut/r304/tp4/q2/ConnectableProperty.class differ diff --git a/out/production/main/fr/univlille/iut/r304/tp4/q2/ObservableProperty.class b/out/production/main/fr/univlille/iut/r304/tp4/q2/ObservableProperty.class new file mode 100644 index 0000000000000000000000000000000000000000..5033e4acddb0b10a48822592ed62737f2b8e5f20 Binary files /dev/null and b/out/production/main/fr/univlille/iut/r304/tp4/q2/ObservableProperty.class differ diff --git a/out/test/test/fr/univlille/iut/r304/tp4/ObserverTestStub.class b/out/test/test/fr/univlille/iut/r304/tp4/ObserverTestStub.class new file mode 100644 index 0000000000000000000000000000000000000000..98f425f29666fde6fccc351ecb81bb2bb7eb8dbd Binary files /dev/null and b/out/test/test/fr/univlille/iut/r304/tp4/ObserverTestStub.class differ diff --git a/out/test/test/fr/univlille/iut/r304/tp4/q1/TestQ1$MockSubject.class b/out/test/test/fr/univlille/iut/r304/tp4/q1/TestQ1$MockSubject.class new file mode 100644 index 0000000000000000000000000000000000000000..b2b5928bb29ab21ccd330a1a89368f542a08cd23 Binary files /dev/null and b/out/test/test/fr/univlille/iut/r304/tp4/q1/TestQ1$MockSubject.class differ diff --git a/out/test/test/fr/univlille/iut/r304/tp4/q1/TestQ1.class b/out/test/test/fr/univlille/iut/r304/tp4/q1/TestQ1.class new file mode 100644 index 0000000000000000000000000000000000000000..96d285541ac9cef5378ac6ed300657cc69a0b0a1 Binary files /dev/null and b/out/test/test/fr/univlille/iut/r304/tp4/q1/TestQ1.class differ diff --git a/out/test/test/fr/univlille/iut/r304/tp4/q2/TestQ1.class b/out/test/test/fr/univlille/iut/r304/tp4/q2/TestQ1.class new file mode 100644 index 0000000000000000000000000000000000000000..123ebbd8be8230d9ba41d93946ca80ff8c6900c7 Binary files /dev/null and b/out/test/test/fr/univlille/iut/r304/tp4/q2/TestQ1.class differ diff --git a/out/test/test/fr/univlille/iut/r304/tp4/q2/TestQ2.class b/out/test/test/fr/univlille/iut/r304/tp4/q2/TestQ2.class new file mode 100644 index 0000000000000000000000000000000000000000..642e02ee83b5ad7150ba4a96340abf9643665c30 Binary files /dev/null and b/out/test/test/fr/univlille/iut/r304/tp4/q2/TestQ2.class differ diff --git a/src/main/java/fr/univlille/iut/r304/tp4/MainExemple.java b/src/main/java/fr/univlille/iut/r304/tp4/MainExemple.java deleted file mode 100644 index db6d1689ef92b24bf28aa1dbdde2f525d2633ada..0000000000000000000000000000000000000000 --- a/src/main/java/fr/univlille/iut/r304/tp4/MainExemple.java +++ /dev/null @@ -1,32 +0,0 @@ -package fr.univlille.iut.r304.tp4; - -import javafx.application.Application; -import javafx.scene.Scene; -import javafx.scene.control.Label; -import javafx.scene.layout.StackPane; -import javafx.stage.Stage; - -public class MainExemple extends Application { - - @Override - public void start(Stage stage) { - Label l = new Label(welcomeMessage()); - Scene scene = new Scene(new StackPane(l), 640, 480); - stage.setScene(scene); - stage.show(); - } - - private String welcomeMessage() { - String javaVersion = System.getProperty("java.version"); - String javafxVersion = System.getProperty("javafx.version"); - - return - "Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".\n" + - " (and powered by maven)\n"; - } - - public static void main(String[] args) { - launch(); - } - -} diff --git a/src/main/java/fr/univlille/iut/r304/tp4/q1/Subject.java b/src/main/java/fr/univlille/iut/r304/tp4/q1/Observable.java similarity index 86% rename from src/main/java/fr/univlille/iut/r304/tp4/q1/Subject.java rename to src/main/java/fr/univlille/iut/r304/tp4/q1/Observable.java index 708757702caf30ac9ed62565da2730fc26f1c71b..7ad1b00967a3a543400461f458f0c655e2b0a9d6 100644 --- a/src/main/java/fr/univlille/iut/r304/tp4/q1/Subject.java +++ b/src/main/java/fr/univlille/iut/r304/tp4/q1/Observable.java @@ -1,7 +1,7 @@ package fr.univlille.iut.r304.tp4.q1; -public abstract class Subject { +public abstract class Observable { protected void notifyObservers() { } diff --git a/src/main/java/fr/univlille/iut/r304/tp4/q1/Observer.java b/src/main/java/fr/univlille/iut/r304/tp4/q1/Observer.java index 51c2e90f895e5ca7eb6fcdd2ff82c6dd37485d74..27d1e30f5e1e5f8a374be4a3b56886a0e92e2934 100644 --- a/src/main/java/fr/univlille/iut/r304/tp4/q1/Observer.java +++ b/src/main/java/fr/univlille/iut/r304/tp4/q1/Observer.java @@ -2,7 +2,7 @@ package fr.univlille.iut.r304.tp4.q1; public interface Observer { - public void update(Subject subj); - public void update(Subject subj, Object data); + void update(Observable subj); + void update(Observable subj, Object data); } diff --git a/src/main/java/fr/univlille/iut/r304/tp4/q1/Timer.java b/src/main/java/fr/univlille/iut/r304/tp4/q1/Timer.java deleted file mode 100644 index 8e94ac11444ee251a576f3e7fc909b902a4c271b..0000000000000000000000000000000000000000 --- a/src/main/java/fr/univlille/iut/r304/tp4/q1/Timer.java +++ /dev/null @@ -1,22 +0,0 @@ -package fr.univlille.iut.r304.tp4.q1; - - -public class Timer { - - public void start() { - } - - public void stopRunning() { - } - - public void attach(Observer observer) { - // methode cree pour que les tests compilent sans erreur - // n'est pas censée rester une fois que vous avez fini Q1.3 - } - - public void detach(Observer observer) { - // methode cree pour que les tests compilent sans erreur - // n'est pas censée rester une fois que vous avez fini Q1.3 - } - -} diff --git a/src/test/java/fr/univlille/iut/r304/tp4/ObserverTestStub.java b/src/test/java/fr/univlille/iut/r304/tp4/ObserverTestStub.java index f7eacbbd6c482174a2d8b3ae9092e280c0111828..f67255b8a0f9f3beec8f9976aa3a9c85f3384104 100644 --- a/src/test/java/fr/univlille/iut/r304/tp4/ObserverTestStub.java +++ b/src/test/java/fr/univlille/iut/r304/tp4/ObserverTestStub.java @@ -1,7 +1,7 @@ package fr.univlille.iut.r304.tp4; import fr.univlille.iut.r304.tp4.q1.Observer; -import fr.univlille.iut.r304.tp4.q1.Subject; +import fr.univlille.iut.r304.tp4.q1.Observable; /** ObserverTestStub * Can tell whether it was notified or not @@ -12,6 +12,7 @@ public class ObserverTestStub implements Observer { protected boolean wasNotified; protected int someValue; + protected static final int expectedValue = 42; public ObserverTestStub() { wasNotified = false; @@ -22,17 +23,24 @@ public class ObserverTestStub implements Observer { return someValue; } + public int getExpectedValue() { + return expectedValue; + } + public void setValue(int someValue) { this.someValue = someValue; } - public void update(Subject subj) { + public void update(Observable subj) { wasNotified = true; } - public void update(Subject subj, Object data) { + public void update(Observable subj, Object data) { wasNotified = true; someValue = (int) data; + if (someValue == expectedValue){ + subj.detach(this); + } } public boolean wasNotified() { diff --git a/src/test/java/fr/univlille/iut/r304/tp4/q1/TestQ1.java b/src/test/java/fr/univlille/iut/r304/tp4/q1/TestQ1.java index 19c818e9004feaee56913a306e67b7b3fb6ae504..f8bfb5001e761faff344490dde0e7d8c9a98997e 100644 --- a/src/test/java/fr/univlille/iut/r304/tp4/q1/TestQ1.java +++ b/src/test/java/fr/univlille/iut/r304/tp4/q1/TestQ1.java @@ -5,9 +5,7 @@ import org.junit.jupiter.api.Test; import fr.univlille.iut.r304.tp4.ObserverTestStub; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; public class TestQ1 { @@ -15,7 +13,7 @@ public class TestQ1 { /** A mock concrete subject * Used to register Observers and notify them */ - protected class MockSubject extends Subject { + protected static class MockSubject extends Observable { public void notifyValue(int i) { this.notifyObservers(i); } @@ -84,4 +82,12 @@ public class TestQ1 { assertEquals(5, observer.getValue()); } + @Test + public void test_detach_on_update_is_safe() { + subject.attach(observer); + subject.notifyValue(observer.getExpectedValue()); + assertTrue(observer.wasNotified()); + subject.notifyValue(5); + assertNotEquals(5,observer.getValue()); + } } diff --git a/src/test/java/fr/univlille/iut/r304/tp4/q1/TestQ3.java b/src/test/java/fr/univlille/iut/r304/tp4/q1/TestQ3.java deleted file mode 100644 index b10ea435ed47c521548b8f9165416705f2c80cf8..0000000000000000000000000000000000000000 --- a/src/test/java/fr/univlille/iut/r304/tp4/q1/TestQ3.java +++ /dev/null @@ -1,69 +0,0 @@ -package fr.univlille.iut.r304.tp4.q1; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import fr.univlille.iut.r304.tp4.ObserverTestStub; - -import static org.junit.jupiter.api.Assertions.*; - - -public class TestQ3 { - - private static final int MORE_THAN_1_SECOND = 1100; // in milliseconds - - /** Two objects used in the tests - */ - protected Timer timer; - protected ObserverTestStub observer; - - @BeforeEach - public void setup() { - timer = new Timer(); - timer.start(); - observer = new ObserverTestStub(); - } - - @AfterEach - public void teardown() { - timer.stopRunning(); - } - - @Test - public void test_timer_notifies_attached_observer() { - assertFalse( observer.wasNotified()); - timer.attach(observer); - try { - Thread.sleep(MORE_THAN_1_SECOND); - } catch (InterruptedException e) { - fail("Exception while waiting"); - } - assertTrue( observer.wasNotified()); - } - - @Test - public void test_timer_does_not_notify_unattached_observer() { - assertFalse( observer.wasNotified()); - try { - Thread.sleep(MORE_THAN_1_SECOND); - } catch (InterruptedException e) { - fail("Exception while waiting"); - } - assertFalse( observer.wasNotified()); - } - - @Test - public void test_timer_does_not_notify_detached_observer() { - assertFalse( observer.wasNotified()); - timer.attach(observer); - timer.detach(observer); - try { - Thread.sleep(MORE_THAN_1_SECOND); - } catch (InterruptedException e) { - fail("Exception while waiting"); - } - assertFalse( observer.wasNotified()); - } - -}