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 und Beobachter 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

Ein Muster -UML -Klassen- und Sequenzdiagramm für das Beobachter -Entwurfsmuster. [6]

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

Uml Klassendiagramm des Beobachtermusters

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

Verweise

  1. ^ Erich Gamma; Richard Helm; Ralph Johnson; John Vlissides (1994). Entwurfsmuster: Elemente wiederverwendbarer objektorientierter Software. Addison Wesley. pp.293ff. ISBN 0-201-63361-2.
  2. ^ "Das Beobachter -Entwurfsmuster - Problem, Lösung und Anwendbarkeit". w3sdesign.com. Abgerufen 2017-08-12.
  3. ^ Vergleich zwischen verschiedenen Beobachtermuster -Implementierungen Moshe Bindler, 2015 (Github)
  4. ^ Unterschiede zwischen Pub/Sub- und Beobachtermuster Das Beobachtermuster von Adi Osmani (Safari -Bücher online)
  5. ^ Das Windows -Programmiererlebnis Charles Petzold, 10. November 1992,, PC Magazine (Google Bücher)
  6. ^ "Das Beobachter -Designmuster - Struktur und Zusammenarbeit". w3sdesign.com. Abgerufen 2017-08-12.
  7. ^ "JQuery - Hören Sie variable Änderungen in JavaScript".
  8. ^ "JQuery - Hören Sie variable Änderungen in JavaScript".

Externe Links

  • Beobachter -Implementierungen in verschiedenen Sprachen bei Wikibooks