Porady: implementowanie dostawcy
Wzorzec projektowy obserwatora wymaga podziału między dostawcą, który monitoruje dane i wysyła powiadomienia oraz co najmniej jeden obserwator, który odbiera powiadomienia (wywołania zwrotne) od dostawcy. W tym temacie omówiono sposób tworzenia dostawcy. Temat pokrewny: Implementowanie obserwatora omawia sposób tworzenia obserwatora.
Aby utworzyć dostawcę
Zdefiniuj dane, które dostawca jest odpowiedzialny za wysyłanie do obserwatorów. Mimo że dostawca i dane wysyłane do obserwatorów mogą być pojedynczym typem, są one zazwyczaj reprezentowane przez różne typy. Na przykład w aplikacji
Temperature
do monitorowania temperatury struktura definiuje dane, które dostawca (reprezentowany przezTemperatureMonitor
klasę zdefiniowaną w następnym kroku) monitoruje i do których obserwatorzy subskrybują.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
Zdefiniuj dostawcę danych, który jest typem implementujący System.IObservable<T> interfejs. Argument typu ogólnego dostawcy jest typem, który dostawca wysyła do obserwatorów. W poniższym przykładzie zdefiniowano klasę
TemperatureMonitor
, która jest skonstruowaną System.IObservable<T> implementacją z argumentem typu ogólnego klasyTemperature
.using System; using System.Collections.Generic; public class TemperatureMonitor : IObservable<Temperature> {
Imports System.Collections.Generic Public Class TemperatureMonitor : Implements IObservable(Of Temperature)
Określ, w jaki sposób dostawca będzie przechowywać odwołania do obserwatorów, aby każdy obserwator mógł być powiadamiany, gdy jest to konieczne. Najczęściej obiekt kolekcji, taki jak obiekt ogólny List<T> , jest używany do tego celu. W poniższym przykładzie zdefiniowano prywatny List<T> obiekt, który jest tworzone w konstruktorze
TemperatureMonitor
klasy.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
Zdefiniuj implementację IDisposable , którą dostawca może wrócić do subskrybentów, aby w dowolnym momencie mógł przestać otrzymywać powiadomienia. W poniższym przykładzie zdefiniowano klasę zagnieżdżona
Unsubscriber
, która jest przekazywana odwołanie do kolekcji subskrybentów i subskrybenta po utworzeniu wystąpienia klasy. Ten kod umożliwia subskrybentowi wywołanie implementacji obiektu IDisposable.Dispose w celu usunięcia się z kolekcji subskrybentów.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
Zaimplementuj metodę IObservable<T>.Subscribe . Metoda jest przekazywana odwołanie do interfejsu System.IObserver<T> i powinna być przechowywana w obiekcie zaprojektowanym do tego celu w kroku 3. Następnie metoda powinna zwrócić implementację opracowaną IDisposable w kroku 4. W poniższym przykładzie pokazano implementację Subscribe metody w
TemperatureMonitor
klasie .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
Powiadamiaj obserwatorów odpowiednio, wywołując ich IObserver<T>.OnNextimplementacje , IObserver<T>.OnErrori IObserver<T>.OnCompleted . W niektórych przypadkach dostawca może nie wywoływać OnError metody w przypadku wystąpienia błędu. Na przykład poniższa
GetTemperature
metoda symuluje monitor, który odczytuje dane temperatury co pięć sekund i powiadamia obserwatorów, czy temperatura zmieniła się o co najmniej ,1 stopnia od poprzedniego odczytu. Jeśli urządzenie nie zgłasza temperatury (czyli jeśli jego wartość ma wartość null), dostawca powiadamia obserwatorów, że transmisja jest zakończona. Należy pamiętać, że oprócz wywołania metodyGetTemperature
każdego obserwatora OnCompleted metoda czyści List<T> kolekcję. W takim przypadku dostawca nie OnError wywołuje metody obserwatorów.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
Przykład
Poniższy przykład zawiera kompletny kod źródłowy do definiowania IObservable<T> implementacji aplikacji do monitorowania temperatury. Obejmuje Temperature
ona strukturę, która jest danymi wysyłanymi do obserwatorów, oraz klasą TemperatureMonitor
, która jest implementacją IObservable<T> .
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