Beobachtermuster
Das Beobachtermuster ist ein Software -Designmuster in welchem an Objekt, benannt die Themabehält eine Liste seiner angehenden Personen, genannt Beobachterund benachrichtigt sie automatisch über staatliche Änderungen, in der Regel durch einen von ihnen Methoden.
Es wird hauptsächlich für die Implementierung verteilt verwendet Handhabung des Events Systeme in "ereignisgesteuerter" Software. In diesen Systemen wird das Subjekt normalerweise als "Strom von Ereignissen" oder "Stream -Quelle von Ereignissen" bezeichnet, während die Beobachter als "Senken von Ereignissen" bezeichnet werden. Die Stream-Nomenklatur spielt auf ein physisches Setup an, bei dem die Beobachter physisch getrennt sind und keine Kontrolle über die emittierten Ereignisse aus dem Subjekt/Stream-Source haben. Dieses Muster passt dann perfekt zu jedem Vorgang, bei dem Daten aus einer Eingabe eingehen, die der CPU beim Start nicht zur Verfügung steht und Blockchains, ...). Die meisten modernen Programmiersprachen umfassen integrierte "Event" -Konstrukte, in denen die Beobachter-Musterkomponenten implementiert werden. Obwohl nicht obligatorisch, würden die meisten Implementierungen von "Beobachtern" Hintergrund-Threads verwenden, die für Subjekte und andere Unterstützungsmechanismen des Kernels (Linux "zuhören Epoll, ...).
Überblick
Das Beobachter -Designmuster ist a Verhaltensmusterunter den dreiundzwanzig bekannten "Bande von vier" Designmuster Beschreibung, wie wiederkehrende Designherausforderungen gelöst werden können, um flexible und wiederverwendbare objektorientierte Software zu entwerfen, d. H. Objekte, die einfacher zu implementieren, zu ändern, zu testen und wiederverwenden zu werden.[1]
Welche Probleme kann das Beobachter -Designmuster lösen?
Das Beobachtermuster befasst sich mit den folgenden Problemen:[2]
- Eine Eins-zu-Viele-Abhängigkeit zwischen Objekten sollte definiert werden, ohne dass die Objekte fest gekoppelt werden.
- Wenn ein Objekt den Status ändert, wird sichergestellt, dass eine offene Anzahl abhängiger Objekte automatisch aktualisiert wird.
- Es sollte möglich sein, dass ein Objekt eine offene Anzahl anderer Objekte informieren kann.
Das Definieren einer Eins-zu-Viele-Abhängigkeit zwischen Objekten durch Definieren eines Objekts (Subjekt), das den Status abhängiger Objekte direkt aktualisiert, ist unflexibel, da es das Subjekt an bestimmte abhängige Objekte passt. Dennoch kann es aus Sicht der Leistung sinnvoll sein oder wenn die Objektimplementierung eng gekoppelt ist (denken Sie an Kernelstrukturen mit niedrigem Niveau, die tausende Male pro Sekunde ausführen). In einigen Szenarien können eng gekoppelte Objekte schwer zu implementieren sein und schwer wiederverwenden, da sie sich auf viele verschiedene Objekte mit unterschiedlichen Schnittstellen beziehen und wissen, wie sie über viele verschiedene Objekte informiert sind. In anderen Szenarien können eng gekoppelte Objekte eine bessere Option sein, da der Compiler Fehler zur Kompilierungszeit erkennen und den Code auf CPU-Anweisungsebene optimieren kann.
Welche Lösung beschreibt das Beobachter -Designmuster?
- Definieren
Thema
undBeobachter
Objekte. - Wenn ein Subjekt den Zustand ändert, werden alle registrierten Beobachter automatisch (und wahrscheinlich asynchron) benachrichtigt und aktualisiert.
Die alleinige Verantwortung eines Faches besteht darin, eine Liste von Beobachtern zu verwalten und sie über staatliche Änderungen zu informieren, indem sie ihre Aufrufe nennen aktualisieren()
Betrieb. Die Verantwortung von Beobachtern besteht darin, sich in einem Thema zu registrieren (und nicht zu registrieren) (um über staatliche Änderungen informiert zu werden) und ihren Zustand zu aktualisieren (synchronisieren Sie ihren Staat mit dem Zustand des Subjekts), wenn sie benachrichtigt werden. Dies macht Subjekte und Beobachter lose gekoppelt. Subjekt und Beobachter haben keine explizite Kenntnis voneinander. Beobachter können zur Laufzeit unabhängig voneinander hinzugefügt und entfernt werden. Diese Interaktion zur Benachrichtigungsregistrierung ist auch als bekannt als Veröffentlichen von Subscribe.
Siehe auch die UML -Klasse und das Sequenzdiagramm unten.
Starke vs. schwache Referenz
Das Beobachtermuster kann verursachen Speicherlecks, bekannt als Verfälschtes Hörerproblem, weil es in einer grundlegenden Implementierung sowohl eine explizite Registrierung als auch eine explizite Dertistrierung erfordert, wie in der Muster entsorgen, weil das Subjekt starke Hinweise auf die Beobachter hat und sie am Leben erhalten. Dies kann durch das Beteiligung des Subjekts verhindert werden Schwache Referenzen an die Beobachter.
Kopplung und typische Pub-Sub-Implementierungen
Typischerweise wird das Beobachtermuster implementiert, sodass das "beobachtete" Subjekt "beobachtet" ist, für das Zustandsänderungen beobachtet (und den Beobachtern mitgeteilt werden). Diese Art der Implementierung wird berücksichtigt "eng verbunden", zwingen sowohl die Beobachter als auch das Subjekt, sich gegenseitig bewusst zu sein und Zugang zu ihren internen Teilen zu haben, um mögliche Probleme von zu erzeugen Skalierbarkeit, Geschwindigkeit, Nachrichtenwahrnehmung und Wartung (auch Ereignis- oder Benachrichtigungsverlust bezeichnet), die mangelnde Flexibilität bei der bedingten Dispersion und ein mögliches Hindernis für die gewünschten Sicherheitsmaßnahmen. In einigen (Nicht-Posation) Implementierungen der Veröffentlichung von Muster (AKA der Pub-Sub Muster), dies wird gelöst, indem ein dedizierter "Nachrichtenwarteschlange" (und manchmal ein zusätzliches "Nachrichtenhandler" -Objekt) als zusätzliche Stufe zwischen dem Beobachter und dem beobachteten Objekt erstellt wird, wodurch die Komponenten entkoppelt werden. In diesen Fällen wird der Beobachter mit dem Beobachtermuster auf den Message Queue -Server zugegriffen, der nur über die erwartete Nachricht (oder nicht in einigen Fällen) weiß, während sie nichts über den Nachrichtensender selbst wissen. Der Absender weiß möglicherweise auch nichts über die Beobachter. Andere Implementierungen des Publish-Subscribe-Musters, die einen ähnlichen Effekt der Benachrichtigung und Kommunikation wie interessierte Parteien erzielen, verwenden das Beobachtermuster überhaupt nicht.[3][4]
In frühen Implementierungen von Multi-Window-Betriebssystemen wie OS/2 und Windows wurden die Begriffe "Veröffentlichen des Subscribe-Musters" und "ereignisgesteuerte Softwareentwicklung" als Synonym für das Beobachtermuster verwendet.[5]
Das Beobachtermuster, wie in der beschrieben Gof -Buch, ist ein sehr grundlegendes Konzept und befasst sich nicht mit dem Entfernen von Interesse an Änderungen des beobachteten "Subjekts" oder der speziellen Logik, die vom beobachteten "Subjekt" vor oder nach der Benachrichtigung der Beobachter durchgeführt werden soll. Das Muster befasst sich auch nicht mit der Aufzeichnung, wenn Änderungsbenachrichtigungen gesendet werden oder garantieren, dass sie empfangen werden. Diese Bedenken werden in der Regel in Nachrichtenwarteschlangensystemen behandelt, von denen das Beobachtermuster nur ein kleiner Teil ist.
Verwandte Muster: Veröffentlichungs -Subscribe -Muster, Vermittler, Singleton.
Entkoppelt
Das Beobachtermuster kann ohne Veröffentlichungsverzeichnis verwendet werden, wie in dem Fall, in dem der Modellstatus häufig aktualisiert wird. Häufige Aktualisierungen können dazu führen, dass die Ansicht nicht mehr reagiert (z. B. durch Anrufen von vielen neu streichen Anrufe); Solche Beobachter sollten stattdessen einen Timer verwenden. Anstatt durch Änderungsnachricht überlastet zu werden, wird der Beobachter in einem regulären Intervall den ungefähren Zustand des Modells darstellen. Diese Art des Beobachters ist besonders nützlich für Fortschrittsriegel, wo sich der Fortschritt des zugrunde liegenden Vorgangs mit mehrmals pro Sekunde ändert.
Struktur
UML -Klassen- und Sequenzdiagramm
In obigem Uml Klassen Diagramm, das Thema
Die Klasse aktualisiert den Status von abhängigen Objekten nicht direkt. Stattdessen, Thema
bezieht sich auf Beobachter
Schnittstelle (aktualisieren()
) zum Aktualisieren des Staates, was das macht Thema
unabhängig davon, wie der Zustand abhängiger Objekte aktualisiert wird. Das Beobachter1
und Beobachter2
Klassen implementieren die Beobachter
Schnittstelle durch Synchronisierung ihres Zustands mit dem Zustand des Subjekts.
Das Uml Sequenzdiagramm Zeigt die Laufzeitinteraktionen an: die Beobachter1
und Beobachter2
Objekte rufen auf Bindungen (this)
an Betreff1
sich registrieren. Annahme, dass der Zustand von Betreff1
Änderungen,Betreff1
Anrufe benachrichtigen()
auf sich.
benachrichtigen()
Anrufe aktualisieren()
auf dem registrierten Beobachter1
und Beobachter2
Objekte, die die geänderten Daten anfordern (GetState ()
) aus Betreff1
um ihren Zustand zu aktualisieren (synchronisieren).
UML -Klassendiagramm
Beispiel
Während die Bibliotheksklassen Java.util.observer
und java.util.observable
existieren, sie wurden in Java 9 veraltet, weil das implementierte Modell ziemlich begrenzt war.
Unten finden Sie ein Beispiel in Java Das nimmt die Tastatureingabe an und behandelt jede Eingangslinie als Ereignis. Wenn eine Zeichenfolge von System.in geliefert wird, ist die Methode NotifyObserver
wird dann aufgerufen, um alle Beobachter über das Auftreten des Ereignisses in Form einer Anrufung ihrer "Update" -Methoden zu informieren.
Java
importieren java.util.list; importieren Java.util.ArrayList; importieren java.util.scanner; Klasse Eventource { Öffentlichkeit Schnittstelle Beobachter { Leere aktualisieren(Saite Veranstaltung); } Privatgelände Finale Aufführen<Beobachter> Beobachter = Neu Anordnungsliste<>(); Privatgelände Leere NotifyObserver(Saite Veranstaltung) { Beobachter.für jeden(Beobachter -> Beobachter.aktualisieren(Veranstaltung)); } Öffentlichkeit Leere Addobserver(Beobachter Beobachter) { Beobachter.hinzufügen(Beobachter); } Öffentlichkeit Leere Scansystemin() { Scanner Scanner = Neu Scanner(System.in); während (Scanner.HasNextline()) { Saite Linie = Scanner.nächste Zeile(); NotifyObserver(Linie); } } }
Öffentlichkeit Klasse Observerdemo { Öffentlichkeit statisch Leere hauptsächlich(Saite[] Args) { System.aus.println("Text eingeben : "); Eventource Eventource = Neu Eventource(); Eventource.Addobserver(Veranstaltung -> { System.aus.println("Empfangene Antwort:" + Veranstaltung); }); Eventource.Scansystemin(); } }
Groovig
Klasse Eventource { Privatgelände Beobachter = [] Privatgelände NotifyObserver(Saite Veranstaltung) { Beobachter.jeder { es(Veranstaltung) } } Leere Addobserver(Beobachter) { Beobachter += Beobachter } Leere Scansystemin() { var Scanner = Neu Scanner(System.in) während (Scanner) { var Linie = Scanner.nächste Zeile() NotifyObserver(Linie) } } } println 'Text eingeben: ' var Eventource = Neu Eventource() Eventource.Addobserver { Veranstaltung -> println "Erhaltene Antwort: $ event" } Eventource.Scansystemin()
Kotlin
importieren java.util.scanner Typen Beobachter = (Veranstaltung: Saite) -> Einheit; Klasse Eventource { Privatgelände var Beobachter = Mutablelistof<Beobachter>() Privatgelände Spaß NotifyObserver(Veranstaltung: Saite) { Beobachter.für jeden { es(Veranstaltung) } } Spaß Addobserver(Beobachter: Beobachter) { Beobachter += Beobachter } Spaß Scansystemin() { val Scanner = Scanner(System.`in ') während (Scanner.HasNext()) { val Linie = Scanner.nächste Zeile() NotifyObserver(Linie) } } }
Spaß hauptsächlich(arg: Aufführen<Saite>) { println("Text eingeben: ") val Eventource = Eventource() Eventource.Addobserver { Veranstaltung -> println("Erhaltene Antwort: $Veranstaltung") } Eventource.Scansystemin() }
Delphi
Verwendet System.Generika.Sammlungen, System.Systeme; Typ Iobserver = Schnittstelle ['{0c8f4c5d-1898-4f24-91da-63f1dd66a692}'] Verfahren Aktualisieren(Const Ein Wert: Saite); Ende; Typ Tobservermanager = Klasse Privatgelände Fobserver: Tlist<Iobserver>; Öffentlichkeit Konstrukteur Schaffen; Überlast; Zerstörer Zerstören; überschreiben; Verfahren NotifyObserver(Const Ein Wert: Saite); Verfahren Addobserver(Const Aobserver: Iobserver); Verfahren UnregisterObsrver(Const Aobserver: Iobserver); Ende; Typ Tistener = Klasse(TinterfacedObject, Iobserver) Privatgelände Fname: Saite; Öffentlichkeit Konstrukteur Schaffen(Const Ein Name: Saite); wieder einführen; Verfahren Aktualisieren(Const Ein Wert: Saite); Ende; Verfahren Tobservermanager.Addobserver(Const Aobserver: Iobserver); Start wenn nicht Fobserver.Enthält(Aobserver) dann Fobserver.Hinzufügen(Aobserver); Ende; Start Freeandnil(Fobserver); vererbt; Ende; Verfahren Tobservermanager.NotifyObserver(Const Ein Wert: Saite); var i: Ganze Zahl; Start zum i : = 0 zu Fobserver.Zählen - 1 tun Fobserver[i].Aktualisieren(Ein Wert); Ende; Verfahren Tobservermanager.UnregisterObsrver(Const Aobserver: Iobserver); Start wenn Fobserver.Enthält(Aobserver) dann Fobserver.Entfernen(Aobserver); Ende; Konstrukteur Tistener.Schaffen(Const Ein Name: Saite); Start vererbt Schaffen; Fname : = Ein Name; Ende; Verfahren Tistener.Aktualisieren(Const Ein Wert: Saite); Start Writeln(Fname + "Hörer erhielt eine Benachrichtigung:" + Ein Wert); Ende; Verfahren Tmyform.BeobachterExampleButtonClick(Absender: Tobjekt); var Ldoornotify: Tobservermanager; Listenerhusband: Iobserver; Listenerwife: Iobserver; Start Ldoornotify : = Tobservermanager.Schaffen; Versuchen Listenerhusband : = Tistener.Schaffen('Ehemann'); Ldoornotify.Addobserver(Listenerhusband); Listenerwife : = Tistener.Schaffen('Ehefrau'); Ldoornotify.Addobserver(Listenerwife); Ldoornotify.NotifyObserver("Jemand klopft an die Tür"); endlich Freeandnil(Ldoornotify); Ende; Ende;
Ausgabe
Ehemann Hörer erhielt eine Benachrichtigung: Jemand klopft an die Tür der Tür Ehefrau Hörer erhielt eine Benachrichtigung: Jemand klopft an die Tür
Python
Ein ähnliches Beispiel in Python:
Klasse Beobachtbar: def __drin__(selbst): selbst._observer = [] def Register_observer(selbst, Beobachter): selbst._observer.anhängen(Beobachter) def notify_observers(selbst, *Args, **Kwargs): zum obs in selbst._observer: obs.benachrichtigen(selbst, *Args, **Kwargs) Klasse Beobachter: def __drin__(selbst, beobachtbar): beobachtbar.Register_observer(selbst) def benachrichtigen(selbst, beobachtbar, *Args, **Kwargs): drucken("Habe", Args, Kwargs, "Aus", beobachtbar) Thema = Beobachtbar() Beobachter = Beobachter(Thema) Thema.notify_observers("Prüfung", KW="Python") # Drucke: Got ('Test',) {'KW': 'Python'} von <__ Main __. Observable Object unter 0x0000019757826fd0>
C#
Öffentlichkeit Klasse Nutzlast { Öffentlichkeit Saite Nachricht { erhalten; einstellen; } }
Öffentlichkeit Klasse Thema : Iobservabel<Nutzlast> { Öffentlichkeit Ilist<Iobserver<Nutzlast>> Beobachter { erhalten; einstellen; } Öffentlichkeit Thema() { Beobachter = Neu Aufführen<Iobserver<Nutzlast>> (); } Öffentlichkeit Identisch Abonnieren(Iobserver<Nutzlast> Beobachter) { wenn (!Beobachter.Enthält(Beobachter)) { Beobachter.Hinzufügen(Beobachter); } Rückkehr Neu Unsubscriber(Beobachter, Beobachter); } Öffentlichkeit Leere Nachricht senden(Saite Botschaft) { für jeden (var Beobachter in Beobachter) { Beobachter.ONNEXT(Neu Nutzlast { Nachricht = Botschaft }); } } }
Öffentlichkeit Klasse Unsubscriber : Identisch { Privatgelände Iobserver<Nutzlast> Beobachter; Privatgelände Ilist<Iobserver<Nutzlast>> Beobachter; Öffentlichkeit Unsubscriber( Iobserver<Nutzlast> Beobachter, Ilist<Iobserver<Nutzlast>> Beobachter) { Dies.Beobachter = Beobachter; Dies.Beobachter = Beobachter; } Öffentlichkeit Leere Entsorgen() { wenn (Beobachter ! = Null && Beobachter.Enthält(Beobachter)) { Beobachter.Entfernen(Beobachter); } } }
Öffentlichkeit Klasse Beobachter : Iobserver<Nutzlast> { Öffentlichkeit Saite Nachricht { erhalten; einstellen; } Öffentlichkeit Leere Aufgehoben() { } Öffentlichkeit Leere OnError(Ausnahme Error) { } Öffentlichkeit Leere ONNEXT(Nutzlast Wert) { Nachricht = Wert.Nachricht; } Öffentlichkeit Identisch Registrieren(Thema Thema) { Rückkehr Thema.Abonnieren(Dies); } }
JavaScript
JavaScript hat veraltet Object.observe
Funktion, die eine genauere Implementierung des Beobachtermusters war.[7] Dies würde Ereignisse beim Wandel zum beobachteten Objekt abfeuern. Ohne veraltete Object.observe
Funktion kann ein Programmierer das Muster weiterhin mit expliziteren Code implementieren:[8]
Lassen Thema = { _Zustand: 0, _observer: [],, hinzufügen: Funktion(Beobachter) { Dies._observer.drücken(Beobachter); }, state: Funktion() { Rückkehr Dies._Zustand; }, setState: Funktion(Wert) { Dies._Zustand = Wert; zum (Lassen i = 0; i < Dies._observer.Länge; i++) { Dies._observer[i].Signal(Dies); } } }; Lassen Beobachter = { Signal: Funktion(Thema) { Lassen aktueller Wert = Thema.state(); Konsole.Protokoll(aktueller Wert); } } Thema.hinzufügen(Beobachter); Thema.setState(10); // Ausgabe in console.log - 10
Siehe auch
- Implizite Berufung
- Client -Server -Modell
- Das Beobachtermuster wird häufig in der verwendet Entity -Component -System Muster
Verweise
- ^ Erich Gamma; Richard Helm; Ralph Johnson; John Vlissides (1994). Entwurfsmuster: Elemente wiederverwendbarer objektorientierter Software. Addison Wesley. pp.293ff. ISBN 0-201-63361-2.
- ^ "Das Beobachter -Entwurfsmuster - Problem, Lösung und Anwendbarkeit". w3sdesign.com. Abgerufen 2017-08-12.
- ^ Vergleich zwischen verschiedenen Beobachtermuster -Implementierungen Moshe Bindler, 2015 (Github)
- ^ Unterschiede zwischen Pub/Sub- und Beobachtermuster Das Beobachtermuster von Adi Osmani (Safari -Bücher online)
- ^ Das Windows -Programmiererlebnis Charles Petzold, 10. November 1992,, PC Magazine (Google Bücher)
- ^ "Das Beobachter -Designmuster - Struktur und Zusammenarbeit". w3sdesign.com. Abgerufen 2017-08-12.
- ^ "JQuery - Hören Sie variable Änderungen in JavaScript".
- ^ "JQuery - Hören Sie variable Änderungen in JavaScript".
Externe Links
- Beobachter -Implementierungen in verschiedenen Sprachen bei Wikibooks