Das Observer Pattern in Java

Das Beobachtermuster in Java

1. Überblick

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

2. Was ist das Beobachtermuster?

Beobachter ist ein Verhaltensmuster. Es gibt die Kommunikation zwischen Objekten an:observable undobservers. An observable is an object which notifies observers about the changes in its state.

Eine Nachrichtenagentur kann beispielsweise Kanäle benachrichtigen, wenn sie Nachrichten erhält. Durch das Empfangen von Nachrichten wird der Status der Nachrichtenagentur geändert und die Kanäle werden benachrichtigt.

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

Definieren wir zunächst die KlasseNewsAgency:

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 ein beobachtbares Element, und wennnews aktualisiert wird, ändert sich der Status vonNewsAgency. Wenn die Änderung eintritt, benachrichtigtNewsAgency die Beobachter über diese Tatsache, indem sie ihreupdate()-Methode aufruft.

To be able to do that, the observable object needs to keep references to the observers, und in unserem Fall ist es die Variablechannels.

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

public class NewsChannel implements Channel {
    private String news;

    @Override
    public void update(Object news) {
        this.setNews((String) news);
    }
}

DieChannel-Schnittstelle hat nur eine Methode:

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

Wenn wir nun eine Instanz vonNewsChannel zur Liste der Beobachter, hinzufügen und den Status vonNewsAgency ändern, wird die Instanz vonNewsChannel 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 vordefinierteObserver-Schnittstelle, die die Implementierung des Beobachtermusters noch einfacher macht. Schauen wir es uns an.

3. Implementierung mitObserver

Diejava.util.Observer-Schnittstelle definiert dieupdate()-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 vonObservable, wie wir unten sehen werden.

Um die beobachtbaren,zu definieren, müssen wir dieObservable-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 dieupdate()-Methode des Beobachters nicht direkt aufrufen müssen. Wir nennen nurstateChanged() undnotifyObservers(), und die KlasseObservableerledigt den Rest für uns.

Außerdem enthält es eine Liste von Beobachtern und legt Methoden zur Verwaltung dieser Liste offen -addObserver() unddeleteObserver().

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

ONewsAgency observable = new ONewsAgency();
ONewsChannel observer = new ONewsChannel();

observable.addObserver(observer);
observable.setNews("news");
assertEquals(observer.getNews(), "news");

Die Benutzeroberfläche vonObserverist nicht perfekt und seit Java 9 veraltet. Einer der Nachteile ist, dassObservable keine Schnittstelle, sondern eine Klasse ist. Deshalb können Unterklassen nicht als Observable verwendet werden.

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

Schauen wir uns dieProperyChangeListener-Schnittstelle an, die empfohlen wird, anstattObserver zu verwenden.

4. Implementierung mitPropertyChangeListener

In this implementation, an observable must keep a reference to the PropertyChangeSupport instance. Es ist hilfreich, die Benachrichtigungen an Beobachter zu senden, wenn eine Eigenschaft der Klasse geändert wird.

Definieren wir das Beobachtbare:

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 diesensupport 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 dritte Argument sind dementsprechend der alte und der neue Wert.

Beobachter solltenPropertyChangeListener implementieren:

public class PCLNewsChannel implements PropertyChangeListener {

    private String news;

    public void propertyChange(PropertyChangeEvent evt) {
        this.setNews((String) evt.getNewValue());
    }
}

Aufgrund derPropertyChangeSupport-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. Fazit

In diesem Artikel haben wir zwei Möglichkeiten untersucht, um das Entwurfsmuster vonObserverin Java zu implementieren, wobei der Ansatz vonPropertyChangeListenerbevorzugt wird.

Der Quellcode für den Artikel istover on GitHub verfügbar.