Das Beobachtermuster in Java

1. Übersicht

In diesem Artikel werden wir das Observer-Muster beschreiben und einige Alternativen zur Java-Implementierung betrachten.

2. Was ist das Beobachtermuster?

Beobachter ist ein Verhaltensentwurfsmuster. Es spezifiziert die Kommunikation zwischen Objekten: beobachtbar und Beobachter . Ein Observable ist ein Objekt, das Beobachter über die Änderungen seines Zustands informiert .

Beispielsweise kann eine Nachrichtenagentur Kanäle benachrichtigen, wenn sie Nachrichten empfängt. Durch das Empfangen von Nachrichten ändert sich der Status der Nachrichtenagentur und die Kanäle werden benachrichtigt.

Mal sehen, wie wir es selbst umsetzen können.

Definieren wir zunächst die NewsAgency- Klasse:

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 ist eine beobachtbare Funktion. Wenn Nachrichten aktualisiert werden, ändert sich der Status von NewsAgency . Wenn die Änderung eintritt, benachrichtigt NewsAgency die Beobachter über diese Tatsache, indem sie ihre update () -Methode aufruft .

Um dies zu tun, muss das beobachtbare Objekt Verweise auf die Beobachter behalten , und in unserem Fall ist es die Kanalvariable .

Lassen Sie uns nun sehen, wie der Beobachter , die Channel- Klasse, aussehen kann. Es sollte die update () -Methode haben, die aufgerufen wird, wenn sich der Status von NewsAgency ändert:

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

Die Kanalschnittstelle hat nur eine Methode:

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

Wenn wir nun eine Instanz hinzufügen Newschannel in der Liste der Beobachter , und ändern Sie den Zustand der Nachrichtenagentur , die Instanz von Newschannel wird aktualisiert:

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

In Java-Kernbibliotheken gibt es eine vordefinierte Observer- Schnittstelle, die die Implementierung des Observer-Musters noch einfacher macht. Schauen wir es uns an.

3. Implementierung mit Observer

Die Schnittstelle java.util.Observer definiert die update () -Methode, sodass Sie sie nicht wie im vorherigen Abschnitt selbst definieren müssen.

Mal sehen, wie wir es in unserer Implementierung verwenden können:

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

Hier kommt das zweite Argument von Observable, wie wir unten sehen werden.

Um das Observable zu definieren , müssen wir die Observable- Klasse von Java erweitern :

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

Beachten Sie, dass wir die update () -Methode des Beobachters nicht direkt aufrufen müssen . Wir rufen einfach setChanged () und notifyObservers () auf , und die Observable- Klasse erledigt den Rest für uns.

Außerdem enthält es eine Liste von Beobachtern und stellt Methoden zur Verwaltung dieser Liste bereit - addObserver () und deleteObserver ().

Um das Ergebnis zu testen, müssen wir nur den Beobachter zu dieser Liste hinzufügen und die Nachrichten festlegen:

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

Die Observer- Schnittstelle ist nicht perfekt und seit Java 9 veraltet. Einer der Nachteile ist, dass Observable keine Schnittstelle, sondern eine Klasse ist. Daher können Unterklassen nicht als Observables verwendet werden.

Außerdem könnte ein Entwickler einige der synchronisierten Methoden des Observable überschreiben und deren Thread-Sicherheit stören.

Schauen wir uns die ProperyChangeListener- Oberfläche an, die anstelle von Observer empfohlen wird .

4. Implementierung mit PropertyChangeListener

In dieser Implementierung muss ein Observable einen Verweis auf die PropertyChangeSupport- Instanz behalten . Es ist hilfreich, die Benachrichtigungen an Beobachter zu senden, wenn eine Eigenschaft der Klasse geändert wird.

Definieren wir das 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; } }

Mit dieser Unterstützung können wir Beobachter hinzufügen und entfernen und sie benachrichtigen, wenn sich der Status der beobachtbaren Elemente ändert:

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

Hier ist das erste Argument der Name der beobachteten Eigenschaft. Das zweite und das dritte Argument sind dementsprechend der alte und der neue Wert.

Beobachter sollten PropertyChangeListener implementieren :

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

Aufgrund der PropertyChangeSupport- Klasse, die die Verkabelung für uns übernimmt, können wir den neuen Eigenschaftswert aus dem Ereignis wiederherstellen.

Testen wir die Implementierung, um sicherzustellen, dass sie auch funktioniert:

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

5. Schlussfolgerung

In diesem Artikel haben wir zwei Möglichkeiten zur Implementierung des Observer- Entwurfsmusters in Java untersucht, wobei der PropertyChangeListener- Ansatz bevorzugt wird.

Der Quellcode für den Artikel ist auf GitHub verfügbar.