Język

Jak zaimplementować obserwatora

Wzorzec projektowy obserwatora wymaga podziału między obserwatorem, który rejestruje się w celu otrzymywania powiadomień, a dostawcą, który monitoruje dane i wysyła powiadomienia do co najmniej jednego obserwatora. W tym artykule omówiono sposób tworzenia obserwatora. Powiązany artykuł How to implement a provider (Jak zaimplementować dostawcę) omawia sposób tworzenia dostawcy.

Tworzenie obserwatora

Aby utworzyć obserwatora, zaimplementuj System.IObserver<T> interfejs. W poniższych krokach opisano każdy element członkowski, który należy zdefiniować.

  1. Zdefiniuj typ obserwatora System.IObserver<T> , który implementuje interfejs.

    Poniższy kod definiuje typ o nazwie TemperatureReporter, który jest skonstruowaną implementacją System.IObserver<T> z argumentem typu ogólnego Temperature.

    namespace TemperatureSample;
    
    public sealed class TemperatureReporter : IObserver<Temperature>
    
    Namespace Global.TemperatureSample
    
        Public NotInheritable Class TemperatureReporter
            Implements IObserver(Of Temperature)
    
  2. Jeśli obserwator musi anulować subskrypcję przed wywołaniami IObserver<T>.OnCompleteddostawcy , zdefiniuj zmienną prywatną do przechowywania IDisposable zwracanego przez IObservable<T>.Subscribeprogram i zdefiniuj metodę subskrypcji.

    Zmienna unsubscriber prywatna przechowuje IDisposable obiekt. Metoda Subscribe wywołuje metodę dostawcy Subscribe i przypisuje zwrócony obiekt do unsubscribermetody .

    namespace TemperatureSample;
    
    public sealed class TemperatureReporter : IObserver<Temperature>
    {
        private IDisposable? _unsubscriber;
        private Temperature? _last;
    
        public void Subscribe(IObservable<Temperature> provider)
        {
            ArgumentNullException.ThrowIfNull(provider);
            _unsubscriber = provider.Subscribe(this);
        }
    
    Namespace Global.TemperatureSample
    
        Public NotInheritable Class TemperatureReporter
            Implements IObserver(Of Temperature)
    
            Private _unsubscriber As IDisposable
            Private _last As Temperature?
    
            Public Sub Subscribe(provider As IObservable(Of Temperature))
                ArgumentNullException.ThrowIfNull(provider)
                _unsubscriber = provider.Subscribe(Me)
            End Sub
    
  3. Zdefiniuj metodę Unsubscribe , która umożliwia obserwatorowi zatrzymanie odbierania powiadomień przed wywołaniem IObserver<T>.OnCompletedprzez dostawcę .

    public void Unsubscribe() => _unsubscriber?.Dispose();
    
    Public Sub Unsubscribe()
        _unsubscriber?.Dispose()
    End Sub
    
  4. Zaimplementuj trzy metody zdefiniowane przez IObserver<T>: IObserver<T>.OnNext, IObserver<T>.OnErrori IObserver<T>.OnCompleted.

    Metody OnError i OnCompleted mogą być implementacjami szkieletowymi. Metoda OnError nie powinna traktować przekazanego Exception jako wyjątku, a OnCompleted może wywołać implementację IDisposable.Dispose dostawcy.

    public void OnCompleted() =>
        Console.WriteLine("Additional temperature data won't be transmitted.");
    
    // OnError is informational; observers shouldn't treat it as an exception to handle.
    public void OnError(Exception error) { }
    
    public void OnNext(Temperature value)
    {
        Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}");
    
        if (_last is Temperature previous)
        {
            TimeSpan elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime();
            Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}");
        }
    
        _last = value;
    }
    
    Public Sub OnCompleted() Implements IObserver(Of Temperature).OnCompleted
        Console.WriteLine("Additional temperature data won't be transmitted.")
    End Sub
    
    ' OnError is informational; observers shouldn't treat it as an exception to handle.
    Public Sub OnError(error_ As Exception) Implements IObserver(Of Temperature).OnError
    End Sub
    
    Public Sub OnNext(value As Temperature) Implements IObserver(Of Temperature).OnNext
        Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}")
    
        If _last.HasValue Then
            Dim previous = _last.Value
            Dim elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime()
            Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}")
        End If
    
        _last = value
    End Sub
    

Kompletny przykład

W poniższym przykładzie przedstawiono kompletny kod źródłowy klasy TemperatureReporter , który zapewnia implementację IObserver<T> aplikacji do monitorowania temperatury.

namespace TemperatureSample;

public sealed class TemperatureReporter : IObserver<Temperature>
{
    private IDisposable? _unsubscriber;
    private Temperature? _last;

    public void Subscribe(IObservable<Temperature> provider)
    {
        ArgumentNullException.ThrowIfNull(provider);
        _unsubscriber = provider.Subscribe(this);
    }

    public void Unsubscribe() => _unsubscriber?.Dispose();

    public void OnCompleted() =>
        Console.WriteLine("Additional temperature data won't be transmitted.");

    // OnError is informational; observers shouldn't treat it as an exception to handle.
    public void OnError(Exception error) { }

    public void OnNext(Temperature value)
    {
        Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}");

        if (_last is Temperature previous)
        {
            TimeSpan elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime();
            Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}");
        }

        _last = value;
    }
}
Namespace Global.TemperatureSample

    Public NotInheritable Class TemperatureReporter
        Implements IObserver(Of Temperature)

        Private _unsubscriber As IDisposable
        Private _last As Temperature?

        Public Sub Subscribe(provider As IObservable(Of Temperature))
            ArgumentNullException.ThrowIfNull(provider)
            _unsubscriber = provider.Subscribe(Me)
        End Sub

        Public Sub Unsubscribe()
            _unsubscriber?.Dispose()
        End Sub

        Public Sub OnCompleted() Implements IObserver(Of Temperature).OnCompleted
            Console.WriteLine("Additional temperature data won't be transmitted.")
        End Sub

        ' OnError is informational; observers shouldn't treat it as an exception to handle.
        Public Sub OnError(error_ As Exception) Implements IObserver(Of Temperature).OnError
        End Sub

        Public Sub OnNext(value As Temperature) Implements IObserver(Of Temperature).OnNext
            Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}")

            If _last.HasValue Then
                Dim previous = _last.Value
                Dim elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime()
                Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}")
            End If

            _last = value
        End Sub
    End Class

End Namespace