Skip to content
Snippets Groups Projects
Commit d4d5c235 authored by Fabien Delecroix's avatar Fabien Delecroix
Browse files

actualisation sujet : début

parent 37683615
No related branches found
No related tags found
No related merge requests found
Showing
with 88 additions and 221 deletions
bin/ 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 ###
# TP4 - Observateur/Observé # TP3 - Observateurs/Observable
Dans ce TP, vous allez vous familiariser avec les notions observateur/observé Dans ce TP, vous allez vous familiariser avec le patron de conception observateurs/observable.
notamment en implémentant les classes nécessaires puis en les utilisant pour D'abord, en implémentant sa structure et son mécanisme.
réaliser une horloge simple. 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 »). La figure suivante vous présente le patron Observateurs (ici « Observer ») / Observable.
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é. 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’Observé chaque fois qu’il veut prévenir d’un évènement. 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() ». Il utilise pour ça sa propre méthode « notifyObservers() ».
```mermaid ```mermaid
classDiagram classDiagram
direction LR direction LR
class Subject class Observable
<<Abstract>> Subject <<Abstract>> Observable
Subject --> "*" Observer : #observers Observable --> "*" Observer : #observers
Subject ..> Observer : notifies Observable ..> Observer : notifies
Subject : +attach(Observer) void Observable : +attach(Observer) void
Subject : +detach(Observer) void Observable : +detach(Observer) void
Subject : #notifyObservers() void Observable : #notifyObservers() void
Subject : #notifyObservers(Object) void Observable : #notifyObservers(Object) void
class Observer class Observer
<<interface>> Observer <<interface>> Observer
Observer : +update(Subject) void Observer : +update(Observable) void
Observer : +update(Subject, Object) 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. Vérifiez la validité de votre implémentation à l'aide des tests fournis.
```java ```java
package fr.univlille.iut.r304.tp4.q1; package fr.univlille.iut.r304.tp4.q1;
public abstract class Subject { public abstract class Observable {
public void attach(Observer obs) public void attach(Observer obs){}
public void detach(Observer obs) public void detach(Observer obs){}
protected void notifyObservers() protected void notifyObservers(){}
protected void notifyObservers(Object data) 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. On souhaite instancier deux règles, qui se mettent à jour à chaque ligne saisie.
Ce TimerThread permettra de déclencher la mise à jour des horloges qui afficheront l’heure. Ces deux règles utiliseront les mêmes saisies mais vérifieront des critères différents.
```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.
```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. 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 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. 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. 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 : 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é ...@@ -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; package fr.univlille.iut.r304.tp4.q2;
public class ConnectableProperty extends ObservableProperty public class ConnectableProperty extends ObservableProperty
public void connectTo(ConnectableProperty other) public void connectTo(ConnectableProperty other){}
public void biconnectTo(ConnectableProperty other) public void biconnectTo(ConnectableProperty other){}
public void unconnectFrom(ConnectableProperty other) public void unconnectFrom(ConnectableProperty other){}
... ...
``` ```
......
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
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();
}
}
package fr.univlille.iut.r304.tp4.q1; package fr.univlille.iut.r304.tp4.q1;
public abstract class Subject { public abstract class Observable {
protected void notifyObservers() { protected void notifyObservers() {
} }
......
...@@ -2,7 +2,7 @@ package fr.univlille.iut.r304.tp4.q1; ...@@ -2,7 +2,7 @@ package fr.univlille.iut.r304.tp4.q1;
public interface Observer { public interface Observer {
public void update(Subject subj); void update(Observable subj);
public void update(Subject subj, Object data); void update(Observable subj, Object data);
} }
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
}
}
package fr.univlille.iut.r304.tp4; package fr.univlille.iut.r304.tp4;
import fr.univlille.iut.r304.tp4.q1.Observer; 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 /** ObserverTestStub
* Can tell whether it was notified or not * Can tell whether it was notified or not
...@@ -12,6 +12,7 @@ public class ObserverTestStub implements Observer { ...@@ -12,6 +12,7 @@ public class ObserverTestStub implements Observer {
protected boolean wasNotified; protected boolean wasNotified;
protected int someValue; protected int someValue;
protected static final int expectedValue = 42;
public ObserverTestStub() { public ObserverTestStub() {
wasNotified = false; wasNotified = false;
...@@ -22,17 +23,24 @@ public class ObserverTestStub implements Observer { ...@@ -22,17 +23,24 @@ public class ObserverTestStub implements Observer {
return someValue; return someValue;
} }
public int getExpectedValue() {
return expectedValue;
}
public void setValue(int someValue) { public void setValue(int someValue) {
this.someValue = someValue; this.someValue = someValue;
} }
public void update(Subject subj) { public void update(Observable subj) {
wasNotified = true; wasNotified = true;
} }
public void update(Subject subj, Object data) { public void update(Observable subj, Object data) {
wasNotified = true; wasNotified = true;
someValue = (int) data; someValue = (int) data;
if (someValue == expectedValue){
subj.detach(this);
}
} }
public boolean wasNotified() { public boolean wasNotified() {
......
...@@ -5,9 +5,7 @@ import org.junit.jupiter.api.Test; ...@@ -5,9 +5,7 @@ import org.junit.jupiter.api.Test;
import fr.univlille.iut.r304.tp4.ObserverTestStub; import fr.univlille.iut.r304.tp4.ObserverTestStub;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestQ1 { public class TestQ1 {
...@@ -15,7 +13,7 @@ public class TestQ1 { ...@@ -15,7 +13,7 @@ public class TestQ1 {
/** A mock concrete subject /** A mock concrete subject
* Used to register Observers and notify them * Used to register Observers and notify them
*/ */
protected class MockSubject extends Subject { protected static class MockSubject extends Observable {
public void notifyValue(int i) { public void notifyValue(int i) {
this.notifyObservers(i); this.notifyObservers(i);
} }
...@@ -84,4 +82,12 @@ public class TestQ1 { ...@@ -84,4 +82,12 @@ public class TestQ1 {
assertEquals(5, observer.getValue()); 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());
}
} }
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());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment