Der Beobachtermodus, auch als Publish/Abonnement-Modus bekannt, wurde von der Vier-Personen-Gruppe (GOF, nämlich Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides) in der Entwurfsmuster von 1994 vorgeschlagen: Die Grundlagen wiederverwendbarer objektorientierter Software "(siehe Seiten 293-313 im Buch für Details). Obwohl dieses Muster eine beträchtliche Geschichte aufweist, ist es immer noch weithin auf eine Vielzahl von Szenarien anwendbar und ist sogar ein wesentlicher Bestandteil der Standard -Java -Bibliothek geworden. Obwohl es bereits viele Artikel über Beobachtermuster gibt, konzentrieren sie sich alle auf die Implementierung in Java, ignorieren jedoch die verschiedenen Probleme, die von Entwicklern auftreten, wenn sie Beobachtermuster in Java verwenden.
Die ursprüngliche Absicht, diesen Artikel zu schreiben, besteht darin, diese Lücke zu schließen: In diesem Artikel wird hauptsächlich die Implementierung des Observer-Musters unter Verwendung der Java8-Architektur eingeführt und komplexe Fragen zu klassischen Mustern auf dieser Basis weiter untersucht, einschließlich anonymer interner Klassen, Lambda-Ausdrücke, Thread-Sicherheit und nicht trivialer Zeitversand. Obwohl der Inhalt dieses Artikels nicht umfassend ist, können viele der komplexen Themen, die an diesem Modell beteiligt sind, nicht in nur einem Artikel erläutert werden. Nach dem Lesen dieses Artikels können die Leser jedoch verstehen, was das Beobachtermuster ist, seine Universalität in Java und wie man mit einigen gemeinsamen Problemen umgeht, wenn sie das Beobachtermuster in Java implementieren.
Beobachtermodus
Gemäß der klassischen Definition von GOF lautet das Thema des Beobachtermusters:
Definiert eine Eins-zu-Viele-Abhängigkeit zwischen Objekten. Wenn sich der Status eines Objekts ändert, werden alle Objekte, die davon abhängen, benachrichtigt und automatisch aktualisiert.
Was bedeutet es? In vielen Softwareanwendungen sind die Zustände zwischen Objekten voneinander abhängig. Wenn sich eine Anwendung beispielsweise auf die numerische Datenverarbeitung konzentriert, können diese Daten über Tabellen oder Diagramme der grafischen Benutzeroberfläche (GUI) angezeigt oder gleichzeitig verwendet werden, dh wenn die zugrunde liegenden Daten aktualisiert werden, müssen die entsprechenden GUI -Komponenten ebenfalls aktualisiert werden. Der Schlüssel zum Problem ist, wie die zugrunde liegenden Daten bei der Aktualisierung der GUI -Komponenten aktualisiert werden und gleichzeitig die Kopplung zwischen den GUI -Komponenten und den zugrunde liegenden Daten minimieren.
Eine einfache und nicht skalierbare Lösung besteht darin, sich auf die Tabellen- und Bild-GUI-Komponenten der Objekte zu beziehen, die diese zugrunde liegenden Daten verwalten, damit die Objekte die GUI-Komponenten bei der Änderung der zugrunde liegenden Daten benachrichtigen können. Offensichtlich zeigte diese einfache Lösung schnell ihre Mängel für komplexe Anwendungen, die mehr GUI -Komponenten verarbeiten. Beispielsweise gibt es 20 GUI -Komponenten, die alle auf zugrunde liegende Daten angewiesen sind. Daher müssen die Objekte, die zugrunde liegende Daten verwalten, Verweise auf diese 20 Komponenten aufrechterhalten. Wenn die Anzahl der von verwandten Daten abhängigen Objekte zunimmt, wird der Grad der Kopplung zwischen Datenverwaltung und Objekten schwer zu kontrollieren.
Eine weitere bessere Lösung besteht darin, Objekte zu registrieren, um Berechtigungen zur Aktualisierung von Daten von Interesse zu erhalten, die der Datenmanager diese Objekte bei der Änderung der Daten benachrichtigt. Lassen Sie das Datenobjekt von Laien den Manager in Laiensgründen sagen: "Bitte benachrichtigen Sie mich, wenn sich die Daten ändert." Darüber hinaus können sich diese Objekte nicht nur registrieren, um Aktualisierungsbenachrichtigungen zu erhalten, sondern auch die Registrierung abzusagen, um sicherzustellen, dass der Datenmanager das Objekt nicht mehr benachrichtigt, wenn sich die Daten ändert. In der ursprünglichen Definition von GOF heißt das Objekt, das als "Beobachter" registriert wird, der entsprechende Datenmanager als "Subjekt" bezeichnet. Wie oben erwähnt, wird der Beobachtermodus auch als Publish-Subscribe-Modus bezeichnet. Es ist zu verstehen, dass ein Kunde den Beobachter über das Ziel abonniert. Wenn der Zielstatus aktualisiert wird, veröffentlicht das Ziel diese Aktualisierungen an den Abonnenten (dieses Entwurfsmuster wird auf eine allgemeine Architektur erweitert, die als Publish-Subscribe-Architektur bezeichnet wird). Diese Konzepte können durch das folgende Klassendiagramm dargestellt werden:
ConcereteObServer verwendet es, um Aktualisierungsstatusänderungen zu erhalten und einen Verweis auf das Conceretesubject an seinen Konstruktor zu übergeben. Dies liefert einen Verweis auf ein bestimmtes Thema für einen bestimmten Beobachter, aus dem Updates bei Änderungen des Zustands erhalten werden können. Einfach ausgedrückt wird der spezifische Beobachter gesagt, er solle das Thema aktualisieren und gleichzeitig die Referenzen in seinem Konstruktor verwenden, um den Zustand des spezifischen Themas zu erhalten, und schließlich diese Suchstatusobjekte unter der Observerstate -Eigenschaft des spezifischen Beobachters zu speichern. Dieser Prozess ist im folgenden Sequenzdiagramm gezeigt:
Professionalisierung klassischer Modelle
Obwohl das Observer -Modell universell ist, gibt es viele spezialisierte Modelle, von denen die häufigsten die folgenden zwei sind:
Bietet einen Parameter für das Statusobjekt, das an die vom Beobachter genannte Aktualisierungsmethode übergeben wird. Im klassischen Modus wird der aktualisierte Zustand direkt vom Subjekt erhalten, wenn der Beobachter mitgeteilt wird, dass sich der Subjektstatus geändert hat. Dies erfordert, dass der Beobachter einen Objektverweis auf den abgerufenen Zustand speichert. Dies bildet eine kreisförmige Referenz, die Referenz des Betonsubjekts zeigt auf seine Beobachterliste und die Referenz von Betonobserver auf das Konkretesubjekt, der den Subjektzustand erhalten kann. Zusätzlich zur Erlangung des aktualisierten Zustands besteht keine Verbindung zwischen dem Beobachter und dem von ihm registrierten Subjekt. Der Beobachter kümmert sich um das Staatsobjekt, nicht um das Thema selbst. Das heißt, in vielen Fällen sind konkretobserver und konkretesubjekts gewaltsam miteinander verbunden. Im Gegenteil, wenn ConcreteSubject die Aktualisierungsfunktion aufruft, wird das Statusobjekt an Betonobserver übergeben und die beiden müssen nicht zugeordnet werden. Der Zusammenhang zwischen konkreterobserver und staatlichem Objekt verringert den Abhängigkeitsgrad zwischen Beobachter und Staat (siehe Martin Fowlers Artikel, um mehr Unterschiede in Bezug auf Assoziation und Abhängigkeit zu erhalten).
Zusammenführen Sie die Subjekt abstrakte Klasse und das ConcreteSubject in eine SingleSubject -Klasse. In den meisten Fällen verbessert die Verwendung abstrakter Klassen im Subjekt die Programmflexibilität und Skalierbarkeit nicht. Daher vereinfacht die Kombination dieser abstrakten Klasse und der konkreten Klasse das Design.
Nachdem diese beiden speziellen Modelle kombiniert wurden, lautet das vereinfachte Klassendiagramm wie folgt:
In diesen speziellen Modellen ist die statische Klassenstruktur stark vereinfacht und die Wechselwirkungen zwischen Klassen werden ebenfalls vereinfacht. Das Sequenzdiagramm zu diesem Zeitpunkt lautet wie folgt:
Ein weiteres Merkmal des Spezialisierungsmodus ist die Entfernung des Beobachtungsvariablen von Mitgliedsbeton von Betonobserver. Manchmal muss der spezifische Beobachter nicht den neuesten Status des Motivs speichern, sondern nur den Status des Subjekts überwachen, wenn der Status aktualisiert wird. Wenn der Beobachter beispielsweise den Wert der Mitgliedsvariablen für die Standardausgabe aktualisiert, kann er den Beobachterstate löschen, wodurch die Assoziation zwischen dem konkretenobserver und der staatlichen Klasse entfernt wird.
Häufigere Namensregeln
Das klassische Modell und sogar das oben erwähnte professionelle Modell verwenden Begriffe wie Anhang, Ablösung und Beobachter, während viele Java -Implementierungen verschiedene Wörterbücher verwenden, einschließlich Register, Unregister, Hörer usw. Es ist erwähnenswert, dass der Zustand ein allgemeiner Begriff für alle Objekte ist, die Hörer benötigt, um Änderungen zu überwachen. Der spezifische Name des Statusobjekts hängt von dem im Beobachtermodus verwendeten Szenario ab. Im Beobachtermodus in der Szene, in der der Hörer das Ereignisvorkommen hört, erhält der registrierte Hörer bei der Ereignis eine Benachrichtigung, wenn das Ereignis auftritt. Das Statusobjekt ist zu diesem Zeitpunkt das Ereignis, das heißt, ob das Ereignis aufgetreten ist.
In den tatsächlichen Anwendungen umfasst die Benennung von Zielen selten ein Thema. Erstellen Sie beispielsweise eine App über einen Zoo, registrieren Sie mehrere Hörer, um die Zoo -Klasse zu beobachten, und erhalten Sie Benachrichtigungen, wenn neue Tiere in den Zoo eintreten. Das Ziel in diesem Fall ist die Zooklasse. Um die Terminologie im Einklang mit der angegebenen Problemdomäne zu halten, wird der Begriff "Subjekt" nicht verwendet, was bedeutet, dass die Zoo -Klasse nicht als Zoosubjekt bezeichnet wird.
Auf die Benennung des Hörers folgt im Allgemeinen das Hörer -Suffix. Zum Beispiel wird der oben erwähnte Hörer, der neue Tiere überwacht, als AnimalDedListener bezeichnet. In ähnlicher Weise wird die Benennung von Funktionen wie Register, Unregister und Benachrichtigung häufig von ihren entsprechenden Hörernamen satt. Beispielsweise werden das Register-, Unregister- und Benachrichtigungsfunktionen von Animedded Listener als RegisteranimaladdedListener, unregisteranimaladdededlistener und notifyAraNaladdededListener ausgezeichnet. Es ist zu beachten, dass der Benachrichtigungsfunktionsname S verwendet wird, da die Benachrichtigungsfunktion mehrere Hörer und eher einen einzelnen Hörer behandelt.
Diese Benennungsmethode erscheint langwierig, und normalerweise registriert ein Thema mehrere Arten von Hörern. In dem oben genannten Beispiel für Zoo im Zoo müssen beispielsweise neben der Registrierung neuer Zuhörer auch die Hörer an Tieren registriert werden, um die Zuhörer zu reduzieren. Zu diesem Zeitpunkt wird es zwei Registerfunktionen geben: (RegisteranimaladdedListener und RegisteranimalRemovedListener. Auf diese Weise wird der Typ des Hörers als Qualifikationsmerkmal verwendet, um die Art von Beobachter anzuzeigen. Eine andere Lösung besteht darin, eine RegisterListener -Funktion zu erstellen und dann zu überlasten, aber Lösung 1 kann mehr wissen, welcher Hörer -Hörer.
Eine andere idiomatische Syntax ist die Verwendung im Präfix anstelle von Update, beispielsweise die Aktualisierungsfunktion, die als Onanimaladded anstelle von Updateanimaladded bezeichnet wird. Diese Situation ist häufiger, wenn der Hörer Benachrichtigungen für eine Sequenz erhält, z. B. ein Tier zur Liste, aber es wird selten verwendet, um separate Daten wie den Namen des Tieres zu aktualisieren.
Als nächstes wird dieser Artikel Javas symbolische Regeln verwenden. Obwohl symbolische Regeln das tatsächliche Design und die Implementierung des Systems nicht ändern, ist es ein wichtiges Entwicklungsprinzip, Begriffe zu verwenden, mit denen andere Entwickler vertraut sind. Das obige Konzept wird unten unter Verwendung eines einfachen Beispiels in der Java 8 -Umgebung erläutert.
Ein einfaches Beispiel
Es ist auch das Beispiel des oben erwähnten Zoo. Verwenden der API -Schnittstelle von Java8 zur Implementierung eines einfachen Systems, wodurch die Grundprinzipien des Observer -Musters erläutert werden. Das Problem wird beschrieben als:
Erstellen Sie einen Systemzoo, sodass Benutzer das Hinzufügen eines neuen Objekttiers anhören und rückgängig machen können, und erstellen Sie einen bestimmten Hörer, der für die Ausgabe des Namens des neuen Tieres verantwortlich ist.
Nach dem vorherigen Erlernen des Beobachtermusters wissen wir, dass wir, um eine solche Anwendung zu implementieren, 4 Klassen erstellen müssen, insbesondere:
ZOO -Klasse: d. H. Das Thema im Muster, das für die Aufbewahrung aller Tiere im Zoo verantwortlich ist und alle registrierten Zuhörer benachrichtigt, wenn sich neue Tiere anschließen.
Tierklasse: repräsentiert ein Tierobjekt.
AnimalDdedListener -Klasse: Das heißt, Observer -Schnittstelle.
PrintNameAnimalAddedListener: Die spezifische Beobachterklasse ist für die Ausgabe des Namens des neu hinzugefügten Tieres verantwortlich.
Zuerst erstellen wir eine Tierklasse, ein einfaches Java -Objekt, das Namensmitgliedsvariablen, Konstrukteure, Getter und Setter -Methoden enthält. Der Code ist wie folgt:
public class Animal {privater String -Name; public Animal (String -Name) {this.name = name;} public String getName () {return this.name;} public void setName (String name) {this.name = name;}}Verwenden Sie diese Klasse, um Tierobjekte darzustellen. Anschließend können Sie die Schnittstelle für AnimalAdded Listener erstellen:
public interface AnimalDdedListener {public void onanimaladded (Tiertier);}Die ersten beiden Klassen sind sehr einfach, daher werde ich sie nicht im Detail vorstellen. Erstellen Sie als nächstes die Zooklasse:
public class zoo {private list <tiere> tiere = new ArrayList <> (); RegisteranimalAddedListener (AnimalAddededListener -Listener) {// den Hörer in die Liste der registrierten Hörer. NotifyAnimalAdded Listeners (Tiertiere) {// Benachrichtigen Sie jeden der Hörer in der Liste der registrierten Hörer Hörer.Diese Analogie ist komplex als die beiden vorherigen. Es enthält zwei Listen, einer wird verwendet, um alle Tiere im Zoo zu speichern, und der andere wird verwendet, um alle Hörer zu speichern. Angesichts der Tatsache, dass die in Tieren und Hörersammlungen gespeicherten Objekte einfach sind, hat dieser Artikel die ArrayList für die Speicherung ausgewählt. Die spezifische Datenstruktur des gespeicherten Hörers hängt vom Problem ab. Zum Beispiel für das Zoo -Problem hier sollten Sie hier, wenn der Hörer Priorität hat, eine andere Datenstruktur auswählen oder den Registeralgorithmus des Hörers umschreiben.
Die Implementierung von Registrierung und Entfernung ist sowohl eine einfache Delegiermethode: Jeder Hörer wird als Parameter aus der Hörliste des Hörers hinzugefügt oder entfernt. Die Implementierung der Benachrichtigungsfunktion erfolgt geringfügig vom Standardformat des Observer -Musters. Es enthält den Eingabeparameter: das neu hinzugefügte Tier, damit die Benachrichtigungsfunktion den neu hinzugefügten Tierverweis an den Hörer übergeben kann. Verwenden Sie die Foreach -Funktion der Streams -API, um die Hörer zu durchqueren und die Theonanimaladded -Funktion für jeden Hörer auszuführen.
In der Addanimal -Funktion werden das neu hinzugefügte Tierobjekt und der Hörer der entsprechenden Liste hinzugefügt. Wenn die Komplexität des Benachrichtigungsprozesses nicht berücksichtigt wird, sollte diese Logik in eine bequeme Anrufmethode enthalten sein. Sie müssen nur in einem Verweis auf das neu hinzugefügte Tierobjekt weitergeben. Aus diesem Grund wird die logische Implementierung des Benachrichtigungshörers in der Funktion "NotifyAnimalAdded Listeners" eingekapselt, die auch in der Implementierung von Addanimal erwähnt wird.
Zusätzlich zu den logischen Fragen der Benachrichtigungsfunktionen ist es erforderlich, das kontroverse Problem bei der Sichtbarkeit von Benachrichtigungsfunktionen zu betonen. In dem klassischen Beobachtermodell ist die Benachrichtigungsfunktion, wie GoF auf Seite 301 der Buchdesignmuster sagte, öffentlich, obwohl dies im klassischen Muster verwendet wird, bedeutet dies nicht, dass es öffentlich sein muss. Die Auswahl der Sichtbarkeit sollte auf der Anwendung basieren. Beispielsweise ist im Zoo -Beispiel dieses Artikels die Benachrichtigungsfunktion vom Typ geschützt und verlangt nicht, dass jedes Objekt eine Benachrichtigung über einen registrierten Beobachter einleitet. Es muss nur sicherstellen, dass das Objekt die Funktion von der übergeordneten Klasse erben kann. Dies ist natürlich nicht genau der Fall. Es ist erforderlich, herauszufinden, welche Klassen die Benachrichtigungsfunktion aktivieren und dann die Sichtbarkeit der Funktion bestimmen können.
Als nächstes müssen Sie die PrintNameAnimaladdedeList -Listener -Klasse implementieren. Diese Klasse verwendet die Methode von System.out.println, um den Namen des neuen Tieres auszugeben. Der spezifische Code lautet wie folgt:
public class PrintNameAnimalAddedListener implements AnimalAddedListener { @Overridepublic void updateAnimalAdded (Animal animal) {// Print the name of the newly added animalSystem.out.println("Added a new animal with name '" + animal.getName() + "'");}}Schließlich müssen wir die Hauptfunktion implementieren, die die Anwendung antreibt:
public class main {public static void main (String [] args) {// den Zoo erstellen, um Tierezoo Zoo = New Zoo (); // Registrieren Sie einen Hörer, der benachrichtigt werden soll, wenn ein Tier hinzugefügt wird. Animal ("Tiger"));}}Die Hauptfunktion erstellt einfach ein Zooobjekt, registriert einen Hörer, der den Tiernamen ausgibt, und erstellt ein neues Tierobjekt, um den registrierten Hörer auszulösen. Die endgültige Ausgabe ist:
Fügte ein neues Tier mit Namen 'Tiger' hinzu
Hörer hinzugefügt
Die Vorteile des Beobachtermodus werden vollständig angezeigt, wenn der Hörer wiederhergestellt und dem Subjekt hinzugefügt wird. Wenn Sie beispielsweise einen Hörer hinzufügen möchten, der die Gesamtzahl der Tiere in einem Zoo berechnet, müssen Sie nur eine bestimmte Hörerklasse erstellen und sie bei der Zoo -Klasse registrieren, ohne dass die Zoo -Klasse geändert wird. Das Hinzufügen des Counting -Listener -CountinganimaladdedListener -Code ist wie folgt:
public class CountingAnimalAddedListener implements AnimalAddedListener { private static int animalsAddedCount = 0;@Overridepublic void updateAnimalAdded (Animal animal) {// Increment the number of animalsSystem.out.println("Total animals added: " + animalsAddedCount);}}Die modifizierte Hauptfunktion lautet wie folgt:
public class main {public static void main (String [] args) {// den Zoo erstellen, um Tierezoo Zoo = New Zoo () zu speichern; // Hörer registrieren, um benachrichtigt zu werden, wenn ein Tier hinzugefügt wird. Benachrichtigen Sie den registrierten Hörernzoo.addanimal (neues Tier ("Tiger");Das Ausgangsergebnis ist:
Hinzufügen eines neuen Tieres mit Namen 'Tiger' Gesamttiere hinzugefügt: 1 Ein neues Tier mit Namen 'Löwen' Gesamttieren hinzugefügt: 2 Hinzufügen eines neuen Tieres mit dem Namen 'Bären' Gesamttiere hinzugefügt: 3
Der Benutzer kann einen beliebigen Listener erstellen, wenn nur der Listener -Registrierungscode geändert wird. Diese Skalierbarkeit liegt hauptsächlich daran, dass das Subjekt mit der Observer -Schnittstelle verbunden ist und nicht direkt mit dem Betonobserver verbunden ist. Solange die Schnittstelle nicht geändert wird, müssen das Betreff der Schnittstelle nicht geändert werden.
Anonyme interne Klassen, Lambda -Funktionen und Hörerregistrierung
Eine wesentliche Verbesserung bei Java 8 ist die Zugabe von funktionalen Merkmalen wie die Zugabe von Lambda -Funktionen. Vor der Einführung der Lambda -Funktion lieferte Java ähnliche Funktionen über anonyme interne Klassen, die in vielen vorhandenen Anwendungen weiterhin verwendet werden. Im Observer -Modus kann ein neuer Hörer jederzeit erstellt werden, ohne eine bestimmte Beobachterklasse zu erstellen. Beispielsweise kann die PrintNameAnimaladdededListener -Klasse in der Hauptfunktion mit anonymer interner Klasse implementiert werden. Der spezifische Implementierungscode lautet wie folgt:
public class main {public static void main (String [] args) {// den Zoo zum Speichern von Tierenzoo Zoo = neuer Zoo (); // Hörer registrieren, um benachrichtigt zu werden, wenn ein Tier hinzugefügt wird. tieresystem.out.println ("Ein neues Tier mit Name '" + Animal.getName () + "'");}}); // Ein Tier hinzufügen, benachrichtigenIn ähnlicher Weise können Lambda -Funktionen auch verwendet werden, um solche Aufgaben zu erledigen:
public class main {public static void main (String [] args) {// den Zoo erstellen, um Tierezoo Zoo = New Zoo (); // Hörer zu benachrichtigen, wenn ein Tier hinzugefügt wird. Listenerszoo.addanimal (neues Tier ("Tiger"));}}Es ist zu beachten, dass die Lambda -Funktion nur für Situationen geeignet ist, in denen nur eine Funktion in der Hörerschnittstelle vorhanden ist. Obwohl diese Anforderung streng erscheint, sind viele Hörer tatsächlich einzelne Funktionen, wie der AnimalDded Listener im Beispiel. Wenn die Schnittstelle mehrere Funktionen hat, können Sie anonyme innere Klassen verwenden.
Es gibt ein solches Problem bei der impliziten Registrierung des erstellten Hörers: Da das Objekt im Rahmen des Registrierungsanrufs erstellt wird, ist es unmöglich, einen Verweis auf einen bestimmten Hörer zu speichern. Dies bedeutet, dass Hörer, die über Lambda -Funktionen oder anonyme interne Klassen registriert sind, nicht widerrufen werden können, da Widerrufsfunktionen einen Verweis auf den registrierten Hörer erfordern. Eine einfache Möglichkeit, dieses Problem zu lösen, besteht darin, einen Verweis auf den registrierten Hörer in der Funktion "RegisteranimalAdded Listener" zurückzugeben. Auf diese Weise können Sie den mit Lambda -Funktionen erstellten Hörer oder anonymen internen Klassen nicht registrieren. Der verbesserte Methodencode lautet wie folgt:
public AnimeddededListener RegisteranimalAddedListener (AnimalAddedListener -Listener) {// Fügen Sie den Hörer der Liste der registrierten Listener. Hörer zurückgeben;}Der Client -Code für die neu gestaltete Funktionsinteraktion lautet wie folgt:
public class main {public static void main (String [] args) {// den Zoo erstellen, um Tierezoo Zoo = New Zoo (); // Hörer zu benachrichtigen, wenn ein Tier hinzugefügt wird, wird anhörgterdener -Hörer = Zoo.registeranimaladdedener (Animal) -> System. "'"); // ein Tier hinzufügen, benachrichtige den registrierten Listenerszoo.Das Ergebnisausgang zu diesem Zeitpunkt wird nur ein neues Tier mit dem Namen 'Tiger' hinzugefügt, da der Hörer abgesagt wurde, bevor das zweite Tier hinzugefügt wird:
Fügte ein neues Tier mit Namen 'Tiger' hinzu
Wenn eine komplexere Lösung übernommen wird, kann die Registerfunktion auch die Empfängerklasse zurückgeben, sodass der nicht register aufgerufene Hörer aufgerufen wird, zum Beispiel:
public class AnimalDdedeLeReceipt {private endgültige AnimalAddededListener -Hörer; public AnimalDdedeLeReReceipt (AnimaladdededListener -Listener) {this.Listener = Listener;} öffentliche endgültige AnimalAddedListener GetListener () {return this.Listener;}}}}}}}}}}}}}}}}}}}}}}}}}}}}}Der Quittung wird als Rückgabewert der Registrierungsfunktion verwendet und die Eingabeparameter der Registrierungsfunktion werden storniert. Zu diesem Zeitpunkt lautet die Zoo -Implementierung wie folgt:
Public Class Zoousingreceipt {// ... vorhandene Attribute und Konstruktor ... public AnimeddedeLeNeReceipt RegisterImalAddedeNerer (AnimalAddedListener -Hörer) {// den Hörer der Liste der registrierten Hörer fügen. (AnimaladdedeLeReReceipt Rezeption) {// Entfernen Sie den Hörer aus der Liste der registrierten Listener.Der oben beschriebene implementierende Implementierungsmechanismus ermöglicht die Speicherung von Informationen für den Anruf an den Hörer beim Widerruf, dh wenn der Widerruf -Registrierungsalgorithmus vom Status des Hörers abhängt, wenn das Betreff den Hörer registriert, wird dieser Status gespeichert. Wenn für die Widerrufsregistrierung nur einen Hinweis auf den vorherigen registrierten Hörer erforderlich ist, erscheint die Empfangstechnologie problematisch und wird nicht empfohlen.
Zusätzlich zu den besonders komplexen spezifischen Zuhörern ist die häufigste Möglichkeit, die Hörer zu registrieren, über Lambda -Funktionen oder über anonyme interne Klassen. Natürlich gibt es Ausnahmen, dh die Klasse, die das Subjekt enthält, implementiert die Observer -Schnittstelle und registriert einen Hörer, der das Referenzziel aufruft. Der Fall wie im folgenden Code gezeigt:
public class zoocontainer implementiert animaladdedlistener {private zoo zoo = new zoo (); public zoocontainer () {// dieses Objekt als listenerhis.zoo.registeranimalAddededListener (this);} public Zoo Getzoo () {thesireT this.zoo;}@Oversidepublic Voiid updataNalAdged (zeitlich) registrieren. {System.out.println("Added animal with name '" + animal.getName() + "'");}public static void main (String[] args) {// Create the zoo containerZooContainer zooContainer = new ZooContainer();// Add an animal notify the innerly notified listenerzooContainer.getZoo().addAnimal(new Animal ("Tiger"));}}Dieser Ansatz ist nur für einfache Fälle geeignet und der Code scheint nicht professionell genug zu sein, und er ist bei modernen Java -Entwicklern immer noch sehr beliebt. Daher ist es notwendig zu verstehen, wie dieses Beispiel funktioniert. Da Zoocontainer die Schnittstelle für AnimalAdded Listener implementiert, kann eine Instanz (oder ein Objekt) von Zoocontainer als AnimalAddedListener registriert werden. In der Zoocontainer -Klasse stellt diese Referenz eine Instanz des aktuellen Objekts dar, nämlich Zookontainer und kann als AnimalAdded -Listener verwendet werden.
Im Allgemeinen sind nicht alle Containerklassen erforderlich, um solche Funktionen zu implementieren, und die Containerklasse, die die Listener -Schnittstelle implementiert, kann die Subjektregistrierungsfunktion nur aufrufen, sondern lediglich die Verweise auf die Registerfunktion als Hörerobjekt übergeben. In den folgenden Kapiteln werden FAQs und Lösungen für Multithread -Umgebungen eingeführt.
OneAPM bietet Ihnen End-to-End-Java-Anwendungsleistungslösungen. Wir unterstützen alle gemeinsamen Java -Frameworks und Anwendungsserver, um schnell System -Engpässe zu entdecken und die Grundursachen für Anomalien zu lokalisieren. Der Einsatz in winzigen Ebenen und Erfahrung sofort war die Java -Überwachung nie einfacher. Um weitere technische Artikel zu lesen, besuchen Sie bitte den offiziellen Technologie -Blog von Oneapm.
Der obige Inhalt führt den relevanten Inhalt der Verwendung von Java 8 ein, um den Observer -Modus zu implementieren (Teil 1). Der nächste Artikel führt die Methode zur Verwendung von Java 8 zur Implementierung des Beobachtermodus (Teil 2) vor. Interessierte Freunde werden weiter lernen und hoffen, dass es für alle hilfreich sein wird!