Nous allons faire un point sur ce que vous savez de ce pattern pour le moment :
- il permet de faire communiquer des objets entre eux ;
- c'est un bon moyen d'éviter le couplage entre les objets.
Ce sont tout de même deux points cruciaux, mais il y a encore un point qui va vous plaire et que vous ne savez pas encore :
tout se fera automatiquement !
Comment les choses vont se passer ? Faisons un point de ce que nous voulons pour notre horloge digitale : celle-ci doit pouvoir avertir notre objet servant à afficher l'heure lorsque celui-ci doit mettre à jour son affichage.
Ici, vu que les horloges du monde entier se mettent à jour toutes les secondes, la nôtre fera de même...
La chose merveilleuse avec ce pattern, c'est que notre horloge ne se contentera pas de mettre un objet au courant que sa valeur a changé, elle pourra même mettre plusieurs observateurs au courant ! !
En fait, pour schématiser au maximum, voyez la relation entre les objets implémentant le pattern observer comme un éditeur de journal et ses clients :
Avec ce schéma, vous pouvez en conclure que notre objet défini comme observable pourra avoir plusieurs objets qui l'observent.
On dit que cet objet a une relation de un à plusieurs vers l'objet Observateur.
Avant de vous expliquer un peu plus le principe de ce pattern, voici le diagramme de classe de notre application :
Avec ce diagramme, vous pouvez vous apercevoir que ce ne sont pas nos instances d'
Horloge ou de
JLabel que nous allons passer, mais des implémentations d'interfaces !
En effet, vous savez maintenant que les classes implémentant une interface peuvent être appelées par le type de l'interface. Dans notre cas, notre classe
Fenetre va implémenter l'interface
Observateur, celle-ci pourra donc être considérée comme un type
Observateur !
Vous constaterez aussi que dans la deuxième interface, celle dédiée à l'objet
Horloge, nous avons trois méthodes :
- une qui permet d'ajouter des observateurs, nous allons donc gérer une collection d'observateurs ;
- une qui permet de retirer les observateurs ;
- et enfin une qui permet de mettre à jour les observateurs !
Grâce à ceci, nos objets ne sont plus liés par leur type respectif, mais par leurs interfaces !
L'interface qui va apporter les méthodes de mise à jour, d'ajout observateur... va travailler avec des objets de type
Observateur !
Ainsi le couplage ne se fait plus directement, mais il s'opère par le biais de ces interfaces.
Tu nous as dit qu'il fallait éviter le couplage !
Oui, mais dans certains cas il est nécessaire... Ici, il faut que nos deux interfaces soient couplées pour que le système fonctionne. De même que, lors du chapitre précédent, nos classes étaient très fortement couplées puisqu'elles devaient travailler ensemble, nous devions donc faire en sorte de ne pas les séparer.
Voici comment l'application va fonctionner.
- Nous allons avoir une instance de la classe Horloge dans notre classe Fenetre.
- Cette dernière va implémenter l'interface Observateur.
- Notre objet Horloge, implémentant l'interface Observable, va attendre d'avoir des objets à prévenir de ses changements.
- Nous allons informer notre horloge que notre fenêtre va l'observer.
- À partir de là, notre objet Horloge va faire le reste : à chaque changement, nous allons appeler la méthode qui met à jour tous les observateurs !
Voici le code source de ces interfaces (j'ai créé un package com.sdz.observer).
Observateur.java
Code : Java1
2
3
4
5 | package com.sdz.observer;
public interface Observateur {
public void update(String hour);
}
|
Observer.java
Code : Java1
2
3
4
5
6
7 | package com.sdz.observer;
public interface Observable {
public void addObservateur(Observateur obs);
public void updateObservateur();
public void delObservateur();
}
|
Voici maintenant le code de nos deux classes, travaillant main dans la main mais ne se rencontrant JAMAIS.
Horloge.java
Code : Java 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 | package com.sdz.model;
import java.util.ArrayList;
import java.util.Calendar;
import com.sdz.observer.Observable;
import com.sdz.observer.Observateur;
public class Horloge extends Thread implements Observable{
//On récupère l'instance d'un calendrier
//celui-ci va nous permettre de récupérer l'heure actuelle
private Calendar cal;
private String hour = "";
//Notre collection d'observateurs !
private ArrayList<Observateur> listObservateur = new ArrayList<Observateur>();
public Horloge(){
Thread t = new Thread(this);
t.start();
}
public void run() {
while(true){
this.cal = Calendar.getInstance();
this.hour = /* Les heures */
this.cal.get(Calendar.HOUR_OF_DAY) + " : "
+
( /* Les minutes */
this.cal.get(Calendar.MINUTE) < 10
? "0" + this.cal.get(Calendar.MINUTE)
: this.cal.get(Calendar.MINUTE)
)
+ " : "
+
( /* Les secondes */
(this.cal.get(Calendar.SECOND)< 10)
? "0"+this.cal.get(Calendar.SECOND)
: this.cal.get(Calendar.SECOND)
);
//On avertit les observateurs que l'heure a été mise à jour !
this.updateObservateur();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* Ajoute un observateur à la liste
*/
public void addObservateur(Observateur obs) {
this.listObservateur.add(obs);
}
/**
* Retire tous les observateurs de la liste
*/
public void delObservateur() {
this.listObservateur = new ArrayList<Observateur>();
}
/**
* Avertit les observateurs que l'observable a changé
* et invoque la méthode update de chaque observateur !
*/
public void updateObservateur() {
for(Observateur obs : this.listObservateur )
obs.update(this.hour);
}
}
|
Fenetre.java
Code : Java 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 | package com.sdz.vue;
import java.awt.BorderLayout;
import java.awt.Font;
import javax.swing.JFrame;
import javax.swing.JLabel;
import com.sdz.model.Horloge;
import com.sdz.observer.Observateur;
public class Fenetre extends JFrame {
private JLabel label = new JLabel();
private Horloge horloge;
public Fenetre(){
/* On initialise notre JFrame */
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setResizable(false);
this.setSize(200, 80);
/* On initialise l'horloge */
this.horloge = new Horloge();
//On place un écouteur sur notre horloge
this.horloge.addObservateur(new Observateur(){
public void update(String hour) {
label.setText(hour);
}
});
/* On initialise notre JLabel */
Font police = new Font("DS-digital", Font.TYPE1_FONT, 30);
this.label.setFont(police);
this.label.setHorizontalAlignment(JLabel.CENTER);
/* On ajoute le JLabel à notre JFrame */
this.getContentPane().add(this.label, BorderLayout.CENTER);
}
/* Méthode main pour lancer le programme */
public static void main(String[] args){
Fenetre fen = new Fenetre();
fen.setVisible(true);
}
}
|
Exécutez ce code et vous verrez que tout fonctionne à merveille !

Vous venez de faire communiquer deux objets entre eux avec un couplage proche de zéro ! Félicitations !
Vous pouvez donc voir de vos yeux que lorsque l'heure change, la méthode
updateObservateur()
est invoquée. Celle-ci parcourt sa collection d'objets
Observateur et appelle la méthode
update(String hour)
de celui-ci. La méthode étant redéfinie dans notre classe
Fenetre afin de mettre à jour le
JLabel, l'heure s'affiche !
La seule chose qui me dérange dans mon exemple tel qu'il est fait, c'est que vous ne voyez pas que l'objet observé met à jour tous les objets qui l'écoutent...
Afin de remédier à cela, nous allons quelque peu modifier notre objet
Fenetre afin que celle-ci n'initialise pas tout de suite l'objet
Horloge, et donc ne lui dise pas tout de suite que celui-ci est observé...
Pour ce faire, nous allons initialiser notre
Horloge avec un mutateur dans la classe
Fenetre et créer une autre classe,
ZFenetre, qui elle, sera codée avec
AWT.
Pour faire très simple, pour passer de swing à AWT, il vous suffit d'utiliser les mêmes objets en enlevant le "J" du début. Donc, JButton devient Button, JFrame devient Frame...
Voici les codes sources de ces deux classes.
ZFenetre.java
Code : Java 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 | package com.sdz.vue;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JLabel;
import com.sdz.model.Horloge;
import com.sdz.observer.Observateur;
public class ZFenetre extends Frame{
private Label label = new Label();
private Horloge horloge;
public ZFenetre(){
this.setLocationRelativeTo(null);
this.setResizable(false);
this.setSize(200, 80);
/* On initialise notre JLabel */
Font police = new Font("DS-digital", Font.TYPE1_FONT, 30);
this.label.setFont(police);
/* On ajoute le JLabel à notre JFrame */
this.add(this.label, BorderLayout.CENTER);
}
public void setHorloge(Horloge horloge) {
this.horloge = horloge;
//On place un écouteur sur notre horloge
this.horloge.addObservateur(new Observateur(){
public void update(String hour) {
label.setText(hour);
}
});
}
}
|
Fenetre.java
Code : Java 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 | package com.sdz.vue;
import java.awt.BorderLayout;
import java.awt.Font;
import javax.swing.JFrame;
import javax.swing.JLabel;
import com.sdz.model.Horloge;
import com.sdz.observer.Observateur;
public class Fenetre extends JFrame {
private JLabel label = new JLabel();
private Horloge horloge;
public Fenetre(){
/* On initialise notre JFrame */
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setResizable(false);
this.setSize(200, 80);
/* On initialise notre JLabel */
Font police = new Font("DS-digital", Font.TYPE1_FONT, 30);
this.label.setFont(police);
this.label.setHorizontalAlignment(JLabel.CENTER);
/* On ajoute le JLabel à notre JFrame */
this.getContentPane().add(this.label, BorderLayout.CENTER);
}
public void setHorloge(Horloge horloge) {
this.horloge = horloge;
//On place un écouteur sur notre horloge
this.horloge.addObservateur(new Observateur(){
public void update(String hour) {
label.setText(hour);
}
});
}
/* Méthode main pour lancer le programme */
public static void main(String[] args){
//Notre horloge UNIQUE
Horloge horloge = new Horloge();
//Notre fenêtre héritant de JFrame
Fenetre fen = new Fenetre();
fen.setVisible(true);
fen.setHorloge(horloge);
ZFenetre zFen = new ZFenetre();
zFen.setVisible(true);
zFen.setHorloge(horloge);
}
}
|
Les deux codes diffèrent légèrement, vous avez dû remarquer que certaines méthodes de notre première fenêtre avaient disparu dans la deuxième...
Ce qui nous donne :
À gauche, la fenêtre faite avec
AWT et à droite, celle faite avec
swing !
Voilà, vous venez d'apprendre à utiliser votre troisième pattern de conception.

Je ne vous ai pas caché que celui-ci sert dans la gestion des événements d'interfaces graphiques. Vous avez remarqué que la syntaxe est identique.
Par contre, je vous ai caché quelque chose : il existe des classes Java qui permettent d'utiliser le pattern observer sans que vous ayez à coder les interfaces !