Le modèle d'observateur en Java

1. Vue d'ensemble

Dans cet article, nous allons décrire le modèle Observer et examiner quelques alternatives d'implémentation Java.

2. Quel est le modèle d'observateur?

Observer est un modèle de conception comportementale. Il spécifie la communication entre objets: observables et observateurs . Un observable est un objet qui informe les observateurs des changements de son état.

Par exemple, une agence de presse peut avertir les chaînes lorsqu'elle reçoit des actualités. Recevoir des nouvelles est ce qui change l'état de l'agence de presse, et cela provoque la notification des chaînes.

Voyons comment nous pouvons l'implémenter nous-mêmes.

Tout d'abord, définissons la classe NewsAgency :

public class NewsAgency { private String news; private List channels = new ArrayList(); public void addObserver(Channel channel) { this.channels.add(channel); } public void removeObserver(Channel channel) { this.channels.remove(channel); } public void setNews(String news) { this.news = news; for (Channel channel : this.channels) { channel.update(this.news); } } }

NewsAgency est une observable, et lorsque les nouvelles sont mises à jour, l'état de NewsAgency change. Lorsque le changement se produit, NewsAgency informe les observateurs de ce fait en appelant leur méthode update () .

Pour pouvoir faire cela, l'objet observable a besoin de garder des références aux observateurs , et dans notre cas, c'est la variable channels .

Voyons maintenant à quoi peut ressembler l'observateur , la classe Channel . Il doit avoir la méthode update () qui est appelée lorsque l'état de NewsAgency change:

public class NewsChannel implements Channel { private String news; @Override public void update(Object news) { this.setNews((String) news); } }

L' interface Channel n'a qu'une seule méthode:

public interface Channel { public void update(Object o); }

Maintenant, si nous ajoutons une instance de NewsChannel à la liste des observateurs , et changeons l'état de NewsAgency , l'instance de NewsChannel sera mise à jour:

NewsAgency observable = new NewsAgency(); NewsChannel observer = new NewsChannel(); observable.addObserver(observer); observable.setNews("news"); assertEquals(observer.getNews(), "news");

Il existe une interface Observer prédéfinie dans les bibliothèques de base Java, ce qui simplifie encore l'implémentation du modèle d'observateur. Regardons ça.

3. Mise en œuvre avec un observateur

L' interface java.util.Observer définit la méthode update () , il n'est donc pas nécessaire de la définir nous-mêmes comme nous l'avons fait dans la section précédente.

Voyons comment nous pouvons l'utiliser dans notre implémentation:

public class ONewsChannel implements Observer { private String news; @Override public void update(Observable o, Object news) { this.setNews((String) news); } } 

Ici, le deuxième argument vient d' Observable comme nous le verrons ci-dessous.

Pour définir l'observable , nous devons étendre la classe Observable de Java :

public class ONewsAgency extends Observable { private String news; public void setNews(String news) { this.news = news; setChanged(); notifyObservers(news); } }

Notez que nous n'avons pas besoin d'appeler directement la méthode update () de l'observateur . Nous appelons simplement setChanged () et notifyObservers () , et la classe Observable fait le reste pour nous.

En outre, il contient une liste d'observateurs et expose des méthodes pour maintenir cette liste - addObserver () et deleteObserver ().

Pour tester le résultat, il suffit d'ajouter l'observateur à cette liste et de définir la nouvelle:

ONewsAgency observable = new ONewsAgency(); ONewsChannel observer = new ONewsChannel(); observable.addObserver(observer); observable.setNews("news"); assertEquals(observer.getNews(), "news");

L' interface d' Observer n'est pas parfaite et est obsolète depuis Java 9. Un de ses inconvénients est que Observable n'est pas une interface mais une classe, c'est pourquoi les sous-classes ne peuvent pas être utilisées comme observables.

De plus, un développeur peut remplacer certaines des méthodes synchronisées d' Observable et perturber leur sécurité des threads.

Regardons l' interface ProperyChangeListener , qui est recommandée au lieu d'utiliser Observer .

4. Implémentation avec PropertyChangeListener

Dans cette implémentation, une observable doit conserver une référence à l' instance PropertyChangeSupport . Il permet d'envoyer les notifications aux observateurs lorsqu'une propriété de la classe est modifiée.

Définissons l'observable:

public class PCLNewsAgency { private String news; private PropertyChangeSupport support; public PCLNewsAgency() { support = new PropertyChangeSupport(this); } public void addPropertyChangeListener(PropertyChangeListener pcl) { support.addPropertyChangeListener(pcl); } public void removePropertyChangeListener(PropertyChangeListener pcl) { support.removePropertyChangeListener(pcl); } public void setNews(String value) { support.firePropertyChange("news", this.news, value); this.news = value; } }

En utilisant ce support , nous pouvons ajouter et supprimer des observateurs, et les notifier lorsque l'état de l'observable change:

support.firePropertyChange("news", this.news, value);

Ici, le premier argument est le nom de la propriété observée. Le deuxième et le troisième arguments sont en conséquence sa valeur ancienne et nouvelle.

Les observateurs doivent implémenter PropertyChangeListener :

public class PCLNewsChannel implements PropertyChangeListener { private String news; public void propertyChange(PropertyChangeEvent evt) { this.setNews((String) evt.getNewValue()); } }

Grâce à la classe PropertyChangeSupport qui effectue le câblage pour nous, nous pouvons restaurer la nouvelle valeur de propriété à partir de l'événement.

Testons l'implémentation pour nous assurer qu'elle fonctionne également:

PCLNewsAgency observable = new PCLNewsAgency(); PCLNewsChannel observer = new PCLNewsChannel(); observable.addPropertyChangeListener(observer); observable.setNews("news"); assertEquals(observer.getNews(), "news");

5. Conclusion

Dans cet article, nous avons examiné deux façons d'implémenter le modèle de conception Observer en Java, l' approche PropertyChangeListener étant préférée.

Le code source de l'article est disponible à l'adresse over sur GitHub.