Observer pattern demystified
The Observer pattern is useful when you need to present data in several different forms at once. The Observer is intended to provide you with a means to define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. The object containing the data is separated from the objects that display the data and the display objects observe changes in that data.
There are basically two different types of objects: a Subject and an Observer. The Subject corresponds to the data object whose state you want to track. The Observer is the object that is interested in changes in the Subject's data. To set up the pattern, Subject classes implement a method for registering Observers and for attaching and detaching them from a collection object.
You also need a Notify(...)
method to notify all registered Observers when the data has changed. For Observers, you define a custom abstract Observer
class to provide clients with a uniform interface, and subclass the implementation details.
Base Implementation
Firstly, we define a public interface called Observer
:
Public Interface Observer
Sub Update(ByVal subj As Object)
End Interface
All our observers (i.e. objects that will be interested in data changes) will have to implement this interface.
Then we define an abstract (MustInherit
in VB.NET) class called Subject
. This is the base class for all objects whose state we are going to watch.
Public MustInherit Class Subject
A collection where we want our observers to be stored.
Private _observers As New ArrayList()
Methods for adding and removing observers
Public Sub AddObserver(ByRef ob As Observer)
_observers.Add(ob)
End Sub
Public Sub RemoveObserver(ByRef ob As Observer)
_observers.Remove(ob)
End Sub
And at last, a public method that will notify all our observers that subject�s state has changed.
Public Sub Notify()
Dim ob As Observer
For Each ob In _observers
ob.Update(Me)
Next
End Sub
End Class
We are through with the abstract part of the implementation.
Live Example
We will implement 3 classes (one of them is only for convenience). First one is Broker
:
Public Class Broker
Private _name As String
Private _balance As Decimal
Private _acc As New BrokerAccount(Me)
Public ReadOnly Property Account() As BrokerAccount
Get
Return _acc
End Get
End Property
Public Sub New()
End Sub
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal Value As String)
_name = Value
End Set
End Property
Public Property Balance() As Decimal
Get
Return _balance
End Get
Set(ByVal Value As Decimal)
_balance = Value
End Set
End Property
End Class
Second one is BrokerAccount
� a class responsible for changing Broker
�s balance.
Public Class BrokerAccount
Inherits Subject
Private _br As Broker
Public Class InvalidBalanceException
Inherits Exception
Public Sub New()
MyBase.New("Broker balance is less than debited amount!")
End Sub
End Class
Public Sub New(ByRef br As Broker)
_br = br
End Sub
Public Sub Credit(ByVal adAmount As Decimal)
_br.Balance += adAmount
MyBase.Notify()
End Sub
Public Sub Debit(ByVal adAmount As Decimal)
If _br.Balance - adAmount < 0 Then
Throw New InvalidBalanceException()
Else
_br.Balance -= adAmount
MyBase.Notify()
End If
End Sub
End Class
And a helper class � collection of Broker objects.
Public Class BrokerCollection
Inherits ArrayList
Default Public Overrides Property Item(ByVal index As Integer) As Object
Get
Return MyBase.Item(index)
End Get
Set(ByVal Value As Object)
If Not TypeOf Value Is Broker Then
Throw New Exception("Can't hold objects of other than Broker type")
Else
MyBase.Item(index) = Value
End If
End Set
End Property
Public Overrides Function Add(ByVal value As Object) As Integer
If Not TypeOf value Is Broker Then
Throw New Exception("Can't hold objects of other than Broker type")
Else
Return MyBase.Add(value)
End If
End Function
End Class
The provided sample (MV_Form
) gives you a quick overview on how to use this pattern in a Windows Forms application:
Public Class Form1
Inherits System.Windows.Forms.Form
Implements MV_Objects.Observer
Form_Load
event: Here we initialize a ListView
control that holds the list of 10 brokers. Each list view is associated with a Broker
object through its Tag
property (very useful one indeed :))
For i = 1 To 10
br = New Broker()
br.Name = "broker " & i
br.Account.AddObserver(Me)
itm = New ListViewItem(New String() {br.Name, br.Balance})
itm.Tag = br
lvBr.Items.Add(itm)
_brcol.Add(br)
Next
The following sub
implements Update
method of the Observer
interface. It finds the entry which is associated with a certain broker in the ListView
and updates the balance
column.
Public Sub UpdateBrokers(ByVal subj As Object) _
Implements MV_Objects.Observer.Update
Dim br As Broker, itm As ListViewItem
For Each itm In lvBr.Items
br = itm.Tag
If br.Account Is subj Then
itm.SubItems(1).Text = br.Balance
End If
Next
End Sub
Also there are 2 buttons and a text box on the form. Their implementation is trivial and not presented in the article for the sake of being concrete.
The End
That�s it! Run the attached sample application to see how it works. You can place a breakpoint on methods Credit
and Debit
of the BrokerAccount
class and step through with a debugger. It�s really a simple but powerful pattern which can used for several more purposes. You are welcome to mention them in the forum :)
One more thing that can be added to this small project is putting each update procedure in a separate thread. It�s OK when the number of objects being watched is small, but fails on other cases. Any hints on this problem (when the number of objects is more than 50,000) will be really appreciated.
Acknowledgements
This article is based on the work by James Maioriello, Ph.D., where he describes basic design patterns. Sorry, I don�t remember where I got it from, but anyway, I did acknowledge it :)