Bewährte Methoden für das Beobachterentwurfsmuster

In .NET wird das Beobachterentwurfsmuster als Eine Reihe von Schnittstellen implementiert. Die System.IObservable<T> Schnittstelle stellt den Datenanbieter dar, der auch für die Bereitstellung einer IDisposable Implementierung verantwortlich ist, die Beobachtern das Abmelden von Benachrichtigungen ermöglicht. Die System.IObserver<T> Schnittstelle stellt den Beobachter dar.

In diesem Artikel werden bewährte Methoden beschrieben, die Sie beim Implementieren des Beobachterentwurfsmusters mit diesen Schnittstellen befolgen.

Erwägen Sie Alternativen vor der Implementierung

Die Schnittstellen IObservable<T> und IObserver<T> eignen sich gut für Push-basierte Benachrichtigungsszenarien, andere .NET Muster eignen sich jedoch möglicherweise besser. Verwenden Sie Für einfache Benachrichtigungen innerhalb einer einzelnen Anwendung Ereignisse. Verwenden Sie IAsyncEnumerable<T>für asynchrone Pull-basierte Sequenzen, bei denen der Verbraucher das Tempo steuert. Verwenden Sie für Producer-Consumer-Muster mit Backpressure System.Threading.Channels. Verwenden Sie für komplexe Ereigniskompositionen, Filterung und Transformation das System.Reactive (Rx.NET)-Paket, anstatt IObservable<T> direkt zu implementieren. Weitere Informationen finden Sie im Designmuster "Observer".

Verwenden eines separaten Typs für Benachrichtigungsdaten

Das Objekt, das die Daten enthält, die der Anbieter an seine Beobachter sendet, entspricht dem generischen Typparameter von IObservable<T> und IObserver<T>. Obwohl dieses Objekt mit der IObservable<T> Implementierung identisch sein kann, definieren Sie es als separaten Typ. Ein dedizierter Datentyp behält die Verantwortlichkeiten des Anbieters getrennt von der Benachrichtigungsnutzlast bei und erleichtert die Entwicklung der API.

Verlassen Sie sich nicht auf die Benachrichtigungsreihenfolge

Die Reihenfolge, in der Beobachter Benachrichtigungen empfangen, ist nicht definiert. Der Anbieter kann jede Methode verwenden, um die Reihenfolge zu bestimmen, also schreiben Sie keine Beobachter, die von der Benachrichtigung vor oder nach einem anderen Beobachter abhängen.

Subscribe und Dispose threadsicher machen

In der Regel implementiert ein Anbieter die IObservable<T>.Subscribe Methode durch Hinzufügen eines Beobachters zu einer Abonnentenliste, die durch ein Auflistungsobjekt dargestellt wird, und implementiert die IDisposable.Dispose Methode, indem der Beobachter aus dieser Liste entfernt wird. Ein Beobachter kann diese Methoden jederzeit aufrufen. Der Vertrag für Anbieter/Beobachter gibt nicht an, wer nach der IObserver<T>.OnCompleted Rückrufmethode für die Abmeldung verantwortlich ist, sodass der Anbieter und der Beobachter möglicherweise beide versuchen, dasselbe Mitglied aus der Liste zu entfernen.

Um Race Conditions zu vermeiden, machen Sie sowohl die Methode Subscribe als auch die Methode Dispose threadsicher. Dies umfasst in der Regel die Verwendung einer gleichzeitigen Sammlung oder einer Sperre. Implementierungen, die nicht threadsicher sind, sollten explizit dokumentieren, dass sie nicht vorhanden sind.

Dokumentieren sie alle zusätzlichen Vertragsgarantien

Geben Sie zusätzliche Garantien in einer Ebene über dem Anbieter-/Beobachtervertrag an. Wenn Sie weitere Anforderungen stellen, weisen Sie klar darauf hin, damit Nutzer nicht über den Beobachtervertrag im Unklaren sind.

Ausnahmen als informativ behandeln

Aufgrund der lockeren Kopplung zwischen einem Datenanbieter und einem Beobachter sollen Ausnahmen im Beobachterentwurfsmuster informational sein. Dieses Merkmal wirkt sich darauf aus, wie Anbieter und Beobachter Ausnahmen behandeln.

OnError nur anrufen, wenn Updates nicht fortgesetzt werden können

Die OnError Methode ist als Informationsmeldung für Beobachter gedacht, ähnlich wie die IObserver<T>.OnNext Methode. Die Methode OnNext liefert einem Beobachter jedoch aktuelle oder aktualisierte Daten, während die Methode OnError anzeigt, dass der Provider keine gültigen Daten bereitstellen kann.

Befolgen Sie die folgenden bewährten Methoden, wenn Sie Ausnahmen behandeln und die OnError Methode aufrufen:

  • Der Anbieter muss seine eigenen Ausnahmen behandeln, wenn er bestimmte Anforderungen hat.
  • Der Anbieter sollte nicht erwarten oder verlangen, dass Beobachter Ausnahmen auf eine bestimmte Weise behandeln.
  • Der Anbieter sollte die OnError Methode aufrufen, wenn sie eine Ausnahme behandelt, die ihre Fähigkeit zum Bereitstellen von Updates kompromittiert. Übergeben Sie Informationen zu solchen Ausnahmen an den Beobachter. In anderen Fällen ist es nicht erforderlich, Beobachter über eine Ausnahme zu informieren.

Nachdem der Anbieter die Methode OnError oder IObserver<T>.OnCompleted aufgerufen hat, sollten keine weiteren Benachrichtigungen mehr erfolgen, und der Anbieter kann seine Beobachter abmelden. Die Beobachter können sich jedoch auch jederzeit abmelden, einschließlich vor und nach Erhalt einer OnErrorIObserver<T>.OnCompleted Benachrichtigung. Das Designmuster des Beobachters bestimmt nicht, ob der Anbieter oder der Beobachter für die Abmeldung verantwortlich ist, sodass beide versuchen, das Abonnement abbestellen zu können. Wenn Beobachter das Abonnement kündigen, werden sie in der Regel aus einer Abonnentensammlung entfernt. In einer Anwendung mit einem einzelnen Thread sollte die IDisposable.Dispose-Implementierung sicherstellen, dass ein Objektverweis gültig ist und, dass das Objekt Mitglied der Abonnentensammlung ist, bevor sie versucht, das Objekt zu entfernen. Verwenden Sie in einer Multithread-Anwendung eine Sperre, um die Beobachtersammlung zu schützen.

OnError-Benachrichtigungen bei Beobachtern als informativ behandeln

Wenn ein Beobachter eine Fehlerbenachrichtigung von einem Anbieter erhält, sollte der Beobachter die Ausnahme als informational behandeln und sollte nicht dazu verpflichtet sein, bestimmte Maßnahmen zu ergreifen.

Befolgen Sie die folgenden bewährten Methoden, wenn Sie auf einen OnError Methodenaufruf eines Anbieters reagieren:

  • Lösen Sie in Implementierungen von Schnittstellen wie OnNext oder OnError keine Ausnahmen aus. Wenn der Beobachter Ausnahmen auslöst, erwarten Sie, dass diese Ausnahmen nicht behandelt werden.
  • Um den Aufrufstapel beizubehalten, sollte ein Beobachter, der ein Exception Objekt auslösen möchte, das an seine OnError Methode übergeben wurde, die Ausnahme umschließen, bevor es ausgelöst wird. Verwenden Sie zu diesem Zweck ein Standard-Ausnahmeobjekt.

Nicht in der Subscribe-Methode abmelden.

Versuchen Sie nicht, die Registrierung in der IObservable<T>.Subscribe Methode aufzuheben, da dies zu einem Nullverweis führen kann.

Einen Observer an einen einzelnen Provider anhängen

Obwohl Sie einen Beobachter an mehrere Anbieter anfügen können, besteht das empfohlene Muster darin, eine IObserver<T> Instanz nur an eine IObservable<T> Instanz anzufügen.