Freigeben über


Vorgehensweise: Implementieren eines Anbieters

Das Entwurfsmuster „Observer“ erfordert eine Trennung zwischen einem Anbieter, der Daten überwacht und Benachrichtigungen sendet, und mindestens einem Beobachter, der Benachrichtigungen (Rückrufe) vom Anbieter empfängt. In diesem Thema wird das Erstellen eines Anbieters behandelt. In dem verwandten Thema Gewusst wie: Implementieren eines Observers wird erläutert, wie ein Observer erstellt wird.

So erstellen Sie einen Anbieter

  1. Definieren Sie die Daten, die vom Anbieter an Observer gesendet werden. Obwohl nur der Anbieter und die an Observer gesendeten Daten einen einzelnen Typ aufweisen können, werden sie in der Regel von unterschiedlichen Typen dargestellt. Beispiel: In einer Anwendung zur Temperaturüberwachung definiert die Temperature-Struktur die Daten, die der Anbieter (dargestellt durch die im nächsten Schritt definierte TemperatureMonitor-Klasse) überwacht, und die abonnierten Observer.

    using System;
    
    public struct Temperature
    {
       private decimal temp;
       private DateTime tempDate;
    
       public Temperature(decimal temperature, DateTime dateAndTime)
       {
          this.temp = temperature;
          this.tempDate = dateAndTime;
       }
    
       public decimal Degrees
       { get { return this.temp; } }
    
       public DateTime Date
       { get { return this.tempDate; } }
    }
    
    Public Structure Temperature
        Private temp As Decimal
        Private tempDate As DateTime
    
        Public Sub New(ByVal temperature As Decimal, ByVal dateAndTime As DateTime)
            Me.temp = temperature
            Me.tempDate = dateAndTime
        End Sub
    
        Public ReadOnly Property Degrees As Decimal
            Get
                Return Me.temp
            End Get
        End Property
    
        Public ReadOnly Property [Date] As DateTime
            Get
                Return tempDate
            End Get
        End Property
    End Structure
    
  2. Definieren Sie den Datenanbieter, der einen Typ zur Implementierung der System.IObservable<T>-Schnittstelle aufweist. Das generische Typargument des Anbieters ist der Typ, den der Anbieter an Observer sendet. Das folgende Beispiel definiert eine TemperatureMonitor-Klasse, die eine erstellte System.IObservable<T>-Implementierung mit einem generischen Typargument von Temperature darstellt.

    using System;
    using System.Collections.Generic;
    
    public class TemperatureMonitor : IObservable<Temperature>
    {
    
    Imports System.Collections.Generic
    
    
    Public Class TemperatureMonitor : Implements IObservable(Of Temperature)
    
  3. Stellen Sie fest, wie der Anbieter Verweise auf Observer speichert, damit jeder Observer ggf. benachrichtigt werden kann. In den meisten Fällen wird hierfür ein Auflistungsobjekt wie etwa ein generisches List<T>-Objekt verwendet. Das folgende Beispiel definiert ein privates List<T>-Objekt, das im Klassenkonstruktor TemperatureMonitor instanziiert wird.

    using System;
    using System.Collections.Generic;
    
    public class TemperatureMonitor : IObservable<Temperature>
    {
       List<IObserver<Temperature>> observers;
    
       public TemperatureMonitor()
       {
          observers = new List<IObserver<Temperature>>();
       }
    
    Imports System.Collections.Generic
    
    
    Public Class TemperatureMonitor : Implements IObservable(Of Temperature)
        Dim observers As List(Of IObserver(Of Temperature))
    
        Public Sub New()
            observers = New List(Of IObserver(Of Temperature))
        End Sub
    
  4. Definieren Sie eine IDisposable-Implementierung, die der Anbieter an Abonnenten zurückgeben kann, sodass diese jederzeit den Empfang von Benachrichtigungen beenden können. Im folgenden Beispiel wird eine geschachtelte Unsubscriber-Klasse definiert, der bei Instanziierung der Klasse ein Verweis auf die Auflistung der Abonnenten und auf den Abonnenten übergeben wird. Durch diesen Code kann der Abonnent die IDisposable.Dispose-Implementierung des Objekts aufrufen, damit es selbst aus der Abonnentenauflistung entfernt wird.

    private class Unsubscriber : IDisposable
    {
       private List<IObserver<Temperature>> _observers;
       private IObserver<Temperature> _observer;
    
       public Unsubscriber(List<IObserver<Temperature>> observers, IObserver<Temperature> observer)
       {
          this._observers = observers;
          this._observer = observer;
       }
    
       public void Dispose()
       {
          if (! (_observer == null)) _observers.Remove(_observer);
       }
    }
    
    Private Class Unsubscriber : Implements IDisposable
        Private _observers As List(Of IObserver(Of Temperature))
        Private _observer As IObserver(Of Temperature)
    
        Public Sub New(ByVal observers As List(Of IObserver(Of Temperature)), ByVal observer As IObserver(Of Temperature))
            Me._observers = observers
            Me._observer = observer
        End Sub
    
        Public Sub Dispose() Implements IDisposable.Dispose
            If _observer IsNot Nothing Then _observers.Remove(_observer)
        End Sub
    End Class
    
  5. Implementieren Sie die IObservable<T>.Subscribe-Methode. Der Methode wird ein Verweis auf die System.IObserver<T>-Schnittstelle übergeben, und sie sollte im Objekt gespeichert werden, das hierfür in Schritt 3 entworfen wurde. Die Methode sollte dann die in Schritt 4 entwickelte IDisposable-Implementierung zurückgeben. Im folgenden Beispiel wird die Implementierung der Subscribe-Methode in der Klasse TemperatureMonitor veranschaulicht.

    public IDisposable Subscribe(IObserver<Temperature> observer)
    {
       if (! observers.Contains(observer))
          observers.Add(observer);
    
       return new Unsubscriber(observers, observer);
    }
    
    Public Function Subscribe(ByVal observer As System.IObserver(Of Temperature)) As System.IDisposable Implements System.IObservable(Of Temperature).Subscribe
        If Not observers.Contains(observer) Then
            observers.Add(observer)
        End If
        Return New Unsubscriber(observers, observer)
    End Function
    
  6. Benachrichtigen Sie ggf. Observer, indem Sie ihre IObserver<T>.OnNext-, IObserver<T>.OnError- und IObserver<T>.OnCompleted-Implementierungen aufrufen. In einigen Fällen kann ein Anbieter beim Auftreten eines Fehlers nicht die OnError-Methode aufrufen. Beispielsweise simuliert die folgende GetTemperature-Methode einen Monitor, der alle fünf Sekunden Temperaturdaten liest und Observer benachrichtigt, wenn sich die Temperatur seit dem letzten Lesevorgang um mindestens 0,1 Grad geändert hat. Wenn das Gerät keine Temperatur meldet (d.h., wenn der Wert null ist), werden Observer durch den Anbieter darüber benachrichtigt, dass die Übertragung abgeschlossen ist. Beachten Sie, dass bei der GetTemperature-Methode nicht nur die OnCompleted-Methode der einzelnen Observer aufgerufen wird, sondern auch die List<T>-Auflistung gelöscht wird. Dabei ruft der Anbieter nicht die OnError-Methode der jeweiligen Observer auf.

    public void GetTemperature()
    {
       // Create an array of sample data to mimic a temperature device.
       Nullable<Decimal>[] temps = {14.6m, 14.65m, 14.7m, 14.9m, 14.9m, 15.2m, 15.25m, 15.2m,
                                    15.4m, 15.45m, null };
       // Store the previous temperature, so notification is only sent after at least .1 change.
       Nullable<Decimal> previous = null;
       bool start = true;
    
       foreach (var temp in temps) {
          System.Threading.Thread.Sleep(2500);
          if (temp.HasValue) {
             if (start || (Math.Abs(temp.Value - previous.Value) >= 0.1m )) {
                Temperature tempData = new Temperature(temp.Value, DateTime.Now);
                foreach (var observer in observers)
                   observer.OnNext(tempData);
                previous = temp;
                if (start) start = false;
             }
          }
          else {
             foreach (var observer in observers.ToArray())
                if (observer != null) observer.OnCompleted();
    
             observers.Clear();
             break;
          }
       }
    }
    
    Public Sub GetTemperature()
        ' Create an array of sample data to mimic a temperature device.
        Dim temps() As Nullable(Of Decimal) = {14.6D, 14.65D, 14.7D, 14.9D, 14.9D, 15.2D, 15.25D, 15.2D,
                                              15.4D, 15.45D, Nothing}
        ' Store the previous temperature, so notification is only sent after at least .1 change.
        Dim previous As Nullable(Of Decimal)
        Dim start As Boolean = True
    
        For Each temp In temps
            System.Threading.Thread.Sleep(2500)
    
            If temp.HasValue Then
                If start OrElse Math.Abs(temp.Value - previous.Value) >= 0.1 Then
                    Dim tempData As New Temperature(temp.Value, Date.Now)
                    For Each observer In observers
                        observer.OnNext(tempData)
                    Next
                    previous = temp
                    If start Then start = False
                End If
            Else
                For Each observer In observers.ToArray()
                    If observer IsNot Nothing Then observer.OnCompleted()
                Next
                observers.Clear()
                Exit For
            End If
        Next
    End Sub
    

Beispiel

Das folgende Beispiel enthält den vollständigen Quellcode, um eine IObservable<T>-Implementierung für eine Anwendung zur Temperaturüberwachung zu definieren. Er enthält die Temperature-Struktur, die die an Observer gesendeten Daten umfasst, und die TemperatureMonitor-Klasse, die die IObservable<T>-Implementierung darstellt.

using System.Threading;
using System;
using System.Collections.Generic;

public class TemperatureMonitor : IObservable<Temperature>
{
   List<IObserver<Temperature>> observers;

   public TemperatureMonitor()
   {
      observers = new List<IObserver<Temperature>>();
   }

   private class Unsubscriber : IDisposable
   {
      private List<IObserver<Temperature>> _observers;
      private IObserver<Temperature> _observer;

      public Unsubscriber(List<IObserver<Temperature>> observers, IObserver<Temperature> observer)
      {
         this._observers = observers;
         this._observer = observer;
      }

      public void Dispose()
      {
         if (! (_observer == null)) _observers.Remove(_observer);
      }
   }

   public IDisposable Subscribe(IObserver<Temperature> observer)
   {
      if (! observers.Contains(observer))
         observers.Add(observer);

      return new Unsubscriber(observers, observer);
   }

   public void GetTemperature()
   {
      // Create an array of sample data to mimic a temperature device.
      Nullable<Decimal>[] temps = {14.6m, 14.65m, 14.7m, 14.9m, 14.9m, 15.2m, 15.25m, 15.2m,
                                   15.4m, 15.45m, null };
      // Store the previous temperature, so notification is only sent after at least .1 change.
      Nullable<Decimal> previous = null;
      bool start = true;

      foreach (var temp in temps) {
         System.Threading.Thread.Sleep(2500);
         if (temp.HasValue) {
            if (start || (Math.Abs(temp.Value - previous.Value) >= 0.1m )) {
               Temperature tempData = new Temperature(temp.Value, DateTime.Now);
               foreach (var observer in observers)
                  observer.OnNext(tempData);
               previous = temp;
               if (start) start = false;
            }
         }
         else {
            foreach (var observer in observers.ToArray())
               if (observer != null) observer.OnCompleted();

            observers.Clear();
            break;
         }
      }
   }
}
Imports System.Threading
Imports System.Collections.Generic


Public Class TemperatureMonitor : Implements IObservable(Of Temperature)
    Dim observers As List(Of IObserver(Of Temperature))

    Public Sub New()
        observers = New List(Of IObserver(Of Temperature))
    End Sub

    Private Class Unsubscriber : Implements IDisposable
        Private _observers As List(Of IObserver(Of Temperature))
        Private _observer As IObserver(Of Temperature)

        Public Sub New(ByVal observers As List(Of IObserver(Of Temperature)), ByVal observer As IObserver(Of Temperature))
            Me._observers = observers
            Me._observer = observer
        End Sub

        Public Sub Dispose() Implements IDisposable.Dispose
            If _observer IsNot Nothing Then _observers.Remove(_observer)
        End Sub
    End Class

    Public Function Subscribe(ByVal observer As System.IObserver(Of Temperature)) As System.IDisposable Implements System.IObservable(Of Temperature).Subscribe
        If Not observers.Contains(observer) Then
            observers.Add(observer)
        End If
        Return New Unsubscriber(observers, observer)
    End Function

    Public Sub GetTemperature()
        ' Create an array of sample data to mimic a temperature device.
        Dim temps() As Nullable(Of Decimal) = {14.6D, 14.65D, 14.7D, 14.9D, 14.9D, 15.2D, 15.25D, 15.2D,
                                              15.4D, 15.45D, Nothing}
        ' Store the previous temperature, so notification is only sent after at least .1 change.
        Dim previous As Nullable(Of Decimal)
        Dim start As Boolean = True

        For Each temp In temps
            System.Threading.Thread.Sleep(2500)

            If temp.HasValue Then
                If start OrElse Math.Abs(temp.Value - previous.Value) >= 0.1 Then
                    Dim tempData As New Temperature(temp.Value, Date.Now)
                    For Each observer In observers
                        observer.OnNext(tempData)
                    Next
                    previous = temp
                    If start Then start = False
                End If
            Else
                For Each observer In observers.ToArray()
                    If observer IsNot Nothing Then observer.OnCompleted()
                Next
                observers.Clear()
                Exit For
            End If
        Next
    End Sub
End Class

Siehe auch