Chapter 28. The Observer Pattern

In this chapter we discuss how you can use the Observer pattern to present data in several forms at once. In our new, more sophisticated windowing world, we often would like to display data in more than one form at the same time and have all of the displays reflect any changes in that data. For example, you might represent stock price changes both as a graph and as a table or list box. Each time the price changes, we'd expect both representations to change at once without any action on our part.

We expect this sort of behavior because there are any number of Windows applications, like Excel, where we see that behavior. Now there is nothing inherent in Windows to allow this activity, and, as you may know, programming directly in Windows in C or C++ is pretty complicated. In VB, however, we can easily use the Observer Design Pattern to make our program behave this way.

The Observer pattern assumes that the object containing the data is separate from the objects that display the data and that these display objects observe changes in that data. This is simple to illustrate, as we see in Figure 28-1.

Data are displayed as a list and in some graphical mode.

Figure 28-1. Data are displayed as a list and in some graphical mode.

When we implement the Observer pattern, we usually refer to the data as the Subject and each of the displays as an Observer. Each of these observers registers its interest in the data by calling a public method in the Subject. Then each observer has a known interface that the subject calls when the data change. We could define these interfaces as follows.

'Interface Observer
Public Sub sendNotify(mesg As String)
End Sub
'-----
'Interface Subject
Public Sub registerInterest(obs As Observer)
End Sub

The advantages of defining these abstract interfaces is that you can write any sort of class objects you want as long as they implement these interfaces and that you can declare these objects to be of type Subject and Observer no matter what else they do.

Watching Colors Change

Let's write a simple program to illustrate how we can use this powerful concept. Our program shows a display form containing three radio buttons named Red, Green, and Blue, as shown in Figure 28-2.

A simple control panel to create red, green, or blue “data”

Figure 28-2. A simple control panel to create red, green, or blue “data”

Note that our main form class implements the Subject interface. That means that it must provide a public method for registering interest in the data in this class. This method is the registerInterest method, which just adds Observer objects to a Collection.

Private Sub Subject_registerInterest(obs As Observer)
 observers.Add obs
End Sub

Now we create two observers, one that displays the color (and its name) and another that adds the current color to a list box.

Private Sub Form_Load()
 Set observers = New Collection
 'create list observer
 Dim lso As New lsObserver
 lso.init Me
 lso.Show
 'create color fram observer
 Dim cfr As New ColorFrame
 cfr.init Me
 cfr.Show

End Sub

When we create our ColorForm window, we register our interest in the data in the main program.

'Class ColorForm
Implements Observer
Public Sub init(s As Subject)
 s.registerInterest Me
End Sub

Private Sub Observer_sendNotify(mesg As String)
Pic.Cls
 Select Case LCase(mesg)
  Case "red"
   Pic.BackColor = vbRed
   Case "green"
   Pic.BackColor = vbGreen
   Case "blue"
   Pic.BackColor = vbBlue
   End Select
  Pic.PSet (300, 600)
  Pic.Print mesg
End Sub

Our list box window is also an observer, and all it has to do is add the color name to the list. The entire class is shown here.

'Class ListObserver
Implements Observer
Public Sub init(s As Subject)
  s.registerInterest Me
End Sub
'-----
Private Sub Observer_sendNotify(mesg As String)
 'add color names to list
 lsColors.AddItem mesg
End Sub

Meanwhile, in our main program, every time someone clicks on one of the radio buttons, it calls the sendNotify method of each Observer who has registered interest in these changes by simply running through the objects in the Observer's Collection.

Private Sub btColor_Click(Index As Integer)
 Dim i As Integer
 Dim mesg As String
 Dim obs As Observer
 mesg = btColor(Index).Caption   'get the button label
 'send it to all the observers
 For i = 1 To observers.Count
   Set obs = observers(i)
   obs.sendNotify mesg
 Next i
End Sub

In the case of the ColorForm observer, the sendNotify method changes the background color and the text string in the form Picturebox. In the case of the ListForm observer, however, it just adds the name of the new color to the list box. We see the final program running in Figure 28-3.

The data control pane generates data that is displayed simultaneously as a colored panel and as a list box. This is a candidate for an Observer pattern.

Figure 28-3. The data control pane generates data that is displayed simultaneously as a colored panel and as a list box. This is a candidate for an Observer pattern.

Writing an Observer in VB.NET

In VB7, we still use the Observer and Subject interfaces to define the interaction between the data and the displays of that data.

Public Interface Observer
    Sub sendNotify(ByVal mesg As String)
End Interface
'-----
Public Interface Subject
    Sub registerInterest(ByVal obs As observer)
End Interface

As in the preceding, the main program with its three radio buttons constitutes the Subject or data class and notifies the observers when the data changes. To make the programming simpler, we add the same event handler to all three radio buttons.

Dim evh As EventHandler = _
        New EventHandler(AddressOf radioHandler)
   AddHandler opRed.Click, evh
   AddHandler opblue.Click, evh
   AddHandler opgreen.Click, evh

Then we send the text of their label to the observers.

Protected Sub RadioHandler(ByVal sender As Object, _
                          ByVal e As EventArgs)
Dim i As Integer
Dim rbut As RadioButton = CType(sender, RadioButton)
 For i = 0 To observers.Count - 1
     Dim obs As Observer = CType(observers(i), observer)
     obs.sendNotify(rbut.Text)
 Next i
End Sub

The list box observer is essentially identical, where we add the text to the list box.

Public Class listObs
    Inherits System.WinForms.Form
    Implements Observer
    Public Sub New(ByVal subj As Subject)
        MyBase.New()
        listObs = Me
        InitializeComponent()
        subj.registerInterest(Me)
    End Sub
    '-----
 Public Sub sendNotify(ByVal mesg As String) _
                      Implements Observer.sendNotify
  lscolors.Items.Add(mesg)
 End Sub
End Class

The Color window observer is a little different in that we paint the text in the paint event handler and change the background color directly in the notify event method.

Public Class ColForm
    Inherits System.WinForms.Form
    Implements Observer
    Private colname As String
    Dim fnt As Font
    Dim bBrush As SolidBrush
    '-----
    Public Sub New(ByVal subj As Subject)
        Me.new()    'Call base constructor
        subj.registerInterest(Me)
        fnt = New Font("arial", 18, Drawing.FontStyle.Bold)
        bBrush = New SolidBrush(Color.Black)
        AddHandler Pic.Paint, _
            New PaintEventHandler(AddressOf paintHandler)
    End Sub
        '-----
    Public Sub sendNotify(ByVal mesg As System.String) _
                        Implements VBNetObserver.Observer.sendNotify
        colname = mesg
        Select Case mesg.ToLower
            Case "red"
                pic.BackColor = color.Red '
            Case "blue"
                pic.BackColor = color.Blue
            Case "green"
                pic.BackColor = color.Green
        End Select
    End Sub
    '-----
    Private Sub paintHandler(ByVal sender As Object, _
                            ByVal e As PaintEventArgs)
        Dim g As Graphics = e.Graphics
        g.DrawString(colname, fnt, bbrush, 20, 40)
    End Sub
End Class

The Message to the Media

Now, what kind of notification should a subject send to its observers? In this carefully circumscribed example, the notification message is the string representing the color itself. When we click on one of the radio buttons, we can get the caption for that button and send it to the observers. This, of course, assumes that all the observers can handle that string representation. In more realistic situations, this might not always be the case, especially if the observers could also be used to observe other data objects. Here we undertake two simple data conversions.

  1. We get the label from the radio button and send it to the observers.

  2. We convert the label to an actual color in the ColorFrame observer.

In more complicated systems, we might have observers that demand specific, but different, kinds of data. Rather than have each observer convert the message to the right data type, we could use an intermediate Adapter class to perform this conversion.

Another problem observers may have to deal with is the case where the data of the central subject class can change in several ways. We could delete points from a list of data, edit their values, or change the scale of the data we are viewing. In these cases we either need to send different change messages to the observers or send a single message and then have the observer ask which sort of change has occurred.

The Observer interface and Subject interface implementation of the Observer pattern

Figure 28-4. The Observer interface and Subject interface implementation of the Observer pattern

Consequences of the Observer Pattern

Observers promote abstract coupling to Subjects. A subject doesn't know the details of any of its observers. However, this has the potential disadvantage of successive or repeated updates to the Observers when there are a series of incremental changes to the data. If the cost of these updates is high, it may be necessary to introduce some sort of change management so the Observers are not notified too soon or too frequently.

When one client makes a change in the underlying data, you need to decide which object will initiate the notification of the change to the other observers. If the Subject notifies all the observers when it is changed, each client is not responsible for remembering to initiate the notification. On the other hand, this can result in a number of small successive updates being triggered. If the clients tell the Subject when to notify the other clients, this cascading notification can be avoided, but the clients are left with the responsibility of telling the Subject when to send the notifications. If one client “forgets,” the program simply won't work properly.

Finally, you can specify the kind of notification you choose to send by defining a number of update methods for the Observers to receive, depending on the type or scope of change. In some cases, the clients will thus be able to ignore some of these notifications.

Thought Question

Thought Question

The VB6 version of our observer example puts up three separate windows. However, unlike the VB7 version, closing one of the windows does not close the other two and end the program. How could you use an observer to ensure that the program shuts down as desired?

Programs on the CD-ROM

Observer VB6 Observer
ObserverVBNet VB7 Observer
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset