Export (0) Print
Expand All

How to: Implement a Provider

The observer design pattern requires a division between a provider, which monitors data and sends notifications, and one or more observers, which receive notifications (callbacks) from the provider. This topic discusses how to create a provider. A related topic, How to: Implement an Observer, discusses how to create an observer.

To create a provider

  1. Define the data that the provider is responsible for sending to observers. Although the provider and the data that it sends to observers can be a single type, they are generally represented by different types. For example, in a temperature monitoring application, the Temperature structure defines the data that the provider (which is represented by the TemperatureMonitor class defined in the next step) monitors and to which observers subscribe.

    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. Define the data provider, which is a type that implements the System.IObservable(Of T) interface. The provider's generic type argument is the type that the provider sends to observers. The following example defines a TemperatureMonitor class, which is a constructed System.IObservable(Of T) implementation with a generic type argument of Temperature.

    Imports System.Collections.Generic
    
    
    Public Class TemperatureMonitor : Implements IObservable(Of Temperature)
    
  3. Determine how the provider will store references to observers so that each observer can be notified when appropriate. Most commonly, a collection object such as a generic List(Of T) object is used for this purpose. The following example defines a private List(Of T) object that is instantiated in the TemperatureMonitor class constructor.

    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. Define an IDisposable implementation that the provider can return to subscribers so that they can stop receiving notifications at any time. The following example defines a nested Unsubscriber class that is passed a reference to the subscribers collection and to the subscriber when the class is instantiated. This code enables the subscriber to call the object's IDisposable.Dispose implementation to remove itself from the subscribers collection.

    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. Implement the IObservable(Of T).Subscribe method. The method is passed a reference to the System.IObserver(Of T) interface and should be stored in the object designed for that purpose in step 3. The method should then return the IDisposable implementation developed in step 4. The following example shows the implementation of the Subscribe method in the TemperatureMonitor 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
    
  6. Notify observers as appropriate by calling their IObserver(Of T).OnNext, IObserver(Of T).OnError, and IObserver(Of T).OnCompleted implementations. In some cases, a provider may not call the OnError method when an error occurs. For example, the following GetTemperature method simulates a monitor that reads temperature data every five seconds and notifies observers if the temperature has changed by at least .1 degree since the previous reading. If the device does not report a temperature (that is, if its value is null), the provider notifies observers that the transmission is complete. Note that, in addition to calling each observer's OnCompleted method, the GetTemperature method clears the List(Of T) collection. In this case, the provider makes no calls to the OnError method of its observers.

    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
    

The following example contains the complete source code for defining an IObservable(Of T) implementation for a temperature monitoring application. It includes the Temperature structure, which is the data sent to observers, and the TemperatureMonitor class, which is the IObservable(Of T) implementation.

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
Show:
© 2015 Microsoft