par , 26/10/2016 à 01h17 (5230 Affichages)
Auteur : Gokan EKINCI
Date de 1ère publication : 2016-10-26
Date de mise à jour : 2016-10-26
Licence : CC BY-NC-SA
L'objectif de cet article sera de vous présenter le design pattern Observer.
Le design pattern Observer permet de notifier des objets Observer lorsqu’un objet Observable est modifié.
L’objet A qui contient la donnée qui sera régulièrement modifiée implémente Observable :
1 2 3 4
| public interface Observable<T> {
public void addObserver(Observer<T> observer);
public void notifyObservers(T data);
} |
L’objet B qui sera notifié par la modification de l’objet A implémente Observer :
1 2 3
| public interface Observer<T> {
public void update(T data);
} |
Cas d’utilisation : Nous avons une classe A « Worker » qui va régulièrement mettre à jour une valeur de type T (un Integer par exemple) dans une méthode work() :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import java.util.*;
public class Worker implements Observable<Integer> {
private List<Observer<Integer>> observers = new ArrayList<>();
@Override
public void addObserver(Observer<Integer> observer) {
observers.add(observer);
}
@Override
public void notifyObservers(Integer data) {
for (Observer<Integer> observer : observers) {
observer.update(data);
}
}
public void work() {
for (int i = 0; i < 10; i++) {
notifyObservers(i);
}
}
} |
Nous avons deux classes B « Screen » qui seront notifiés par la modification du Worker, l’un des écrans va écrire la donnée sur la sortie standard « out », et l’autre va écrire la donnée sur la sortie d’erreur « err » :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| interface Screen extends Observer<Integer> {
public void update(Integer data);
}
class ScreenOut implements Screen {
@Override
public void update(Integer data) {
System.out.println(data);
}
}
class ScreenErr implements Screen {
@Override
public void update(Integer data) {
System.err.println(data);
}
} |
Exécutons notre code :
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Main {
public static void main(String[] args) {
// On initialise nos observers :
Screen screenOut = new ScreenOut();
Screen screenErr = new ScreenErr();
// On initialise notre observable :
Worker worker = new Worker();
worker.addObserver(screenOut);
worker.addObserver(screenErr);
worker.work();
}
} |
Analyse : Le code affiche les chiffres 0 à 9 dans la sortie standard « out » et « err ».
Pour les plus curieux : Le langage Java intègre le pattern Observer depuis la version 1.0 du JDK, reprenons les exemples précédents avec les classes standards Observable et Observer.
Soit la classe Worker :
1 2 3 4 5 6 7 8 9 10 11
| import java.util.Observable;
public class Worker extends Observable {
public void work() {
for (int i = 0; i < 10; i++) {
setChanged();
notifyObservers(i);
}
}
} |
Soit les classes screens :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import java.util.Observer;
interface Screen extends Observer {
public void update(Observable o, Object data);
}
class ScreenOut implements Screen {
@Override
public void update(Observable o, Object data) {
System.out.println(data);
}
}
class ScreenErr implements Screen {
@Override
public void update(Observable o, Object data) {
System.err.println(data);
}
} |
La classe pour exécuter notre code (cf : Main) ne change pas. Le résultat avec l'exemple précédent est bien sûr le même.
Conclusion : Les classes du pattern Observer inclus dans le JDK permettent d’avoir un code un poil plus court pour notre classe Worker, cependant elle a non seulement l’inconvénient d’être une classe que l’on doit hériter (ce n’est pas très pratique car l’héritage multiple n’existe pas en Java et par conséquent la classe Workerne peut pas hériter d’une autre classe), mais en plus nous sommes forcés de caster explicitement le paramètre Object de la méthode update() dans notre Observer.