Friday, May 31, 2013

Implementing Observer Design Pattern in VBA with Events

I have "known" VBA Events for years. Once I created one application for hosting some data updating process. To automatize that process, I set a task for opening Excel workbook with Windows Scheduler. As soon as Scheduler was opening Excel workbook in the morning, Workbook_Open() method was kicking in and the magic was happening.

To be honest, for a long time I was lazy to dig deeper and also did not find any other "real" use for VBA Events, except these common Events for Excel workbook objects. I think this is not anything uncommon. If you use Google to find some practical examples, you will find a lot of examples, but they are almost always some examples with VBA User Forms. Anyway, at some point I was doing some semi-serious woodshedding with Observer Design Pattern and started to think about Events again. I started to study these a bit deeper and found out one pretty nice thing: I could create my own Events and use those in my classes. This current post is a result of that discovery.

In this post, I am opening up one possible Observer Design Pattern implementation with VBA Event mechanism. Remember to check my previous post on doing the same thing without VBA Events http://mikejuniperhill.blogspot.fi/2013/05/implementing-observer-design-pattern-in.html

First we create data source class (VBA Class Module, name = DataSource):

Option Explicit
'
Public Event dataFeedUpdate(ByVal marketData As Double)
'
Public Function getMarketDataUpdate(ByVal marketData As Double)
    '
    RaiseEvent dataFeedUpdate(marketData)
End Function
'

Then we create class for all "observers" (VBA Class Module, name = DataObserver):

Option Explicit
'
Private WithEvents source As DataSource
'
Public Function registerPublisher(ByRef dataFeed As DataSource)
    '
    Set source = dataFeed
    Debug.Print "Observer " & ObjPtr(Me) & " registered data feed"
End Function
'
Public Function unRegisterPublisher()
    '
    Set source = Nothing
    Debug.Print "Observer " & ObjPtr(Me) & " unregistered data feed"
End Function
'
Private Sub source_dataFeedUpdate(ByVal marketData As Double)
    '
    Debug.Print "Market data update from observer " & ObjPtr(Me) & ": " & marketData
End Sub
'

Then again, we create one tester program (VBA Standard Module). In tester program, I have been creating one test run, in which observers are registering/unregistering data source, which is sending data feed updates for observers:


Option Explicit
'
Public Sub tester()
    '
    ' create data source object and two observers
    Dim source As New DataSource
    Dim observer1 As New DataObserver
    Dim observer2 As New DataObserver
    '
    ' register datafeed publisher for both observers
    observer1.registerPublisher source
    observer2.registerPublisher source
    '
    ' send data feed
    source.getMarketDataUpdate 100.25
    source.getMarketDataUpdate 100.15
    '
    ' observer2 unregistering, send data feed
    observer2.unRegisterPublisher
    source.getMarketDataUpdate 100.1
    '
    ' observer1 unregistering, send data feed
    observer1.unRegisterPublisher
    source.getMarketDataUpdate 100.1
    '
    ' observer1 re-registering, send data feed
    observer1.registerPublisher source
    source.getMarketDataUpdate 100.2
    '
    ' observer2 re-registering, send data feed
    observer2.registerPublisher source
    source.getMarketDataUpdate 100.25
    '
    ' destroy datafeed and both observers
    Set observer2 = Nothing
    Set observer1 = Nothing
    Set source = Nothing
End Sub
'


Resulting Immediate Window printout (observer1 in blue, observer2 in red):
Observer 237412240 registered data feed
Observer 237411920 registered data feed
Market data update from observer 237412240: 100,25
Market data update from observer 237411920: 100,25
Market data update from observer 237412240: 100,15
Market data update from observer 237411920: 100,15
Observer 237411920 unregistered data feed

Market data update from observer 237412240: 100,1
Observer 237412240 unregistered data feed
Observer 237412240 registered data feed
Market data update from observer 237412240: 100,2

Observer 237411920 registered data feed
Market data update from observer 237412240: 100,25
Market data update from observer 237411920: 100,25


One more thing: when you create your custom event, the object (DataObserver) in which the Event-related function is going to be called, must have reference to its caller (DataSource). Otherwise calling object (DataSource) do not know, what object it should call. I am doing that "binding" in registerPublisher function. This issue was actually causing a lot of grey hairs for me, until I got the point.

This was another possible implementation of Observer DP in VBA by using Event mechanism. Nice to know also. Have a nice weekend!
-Mike



No comments:

Post a Comment