Access JumpStart 2.0 | Blog

A Rapid Development Framework for Microsoft Access

I was excited yesterday to discover that I wanted an interface and how this might help with my TDD. I refactored the FormListener to have an interface.

Here’s why I’m so excited.

Now I can have a live version of the Listener AND a test version of the listener that are different, but have to meet the requirements of the interface and are interchangeable in terms of the functionality I want any particular FormListener to work.

So, again, the interface for the FormListener (saved with the name IFormListener) is currrently VERY simple:

'@Interface

Option Explicit

Public Sub Setup(theForm As Access.Form)
End Sub

This simply means that any class I create that uses the line will not compile unless it includes the elements listed in the interface:

Implements IFormListener

Currently, the only thing this means is that a listener must have a public Setup routine that accepts an Access Form object. But, I can add things to that interface as I continue and this will require all classes based on the interface to have those elements. For example, I will add a Public Event at some point in the future which will require all the classes based on the interface to expose that event.

As long as each interface will expose the things I want the coder to ask it to do, this means I can create other versions of the listener that have different internal code.

At this point I can now create a new Test class for the FormListener based on IFormListener and add new functions for the testing which won’t be required for another class for the FormListener that will be used in the live environment.

So, I will rename the current class that I originally called FormListener to: TestFormListener. Again, here is the code of the now newly named TestFormListener:

Option Compare Database
Option Explicit

Implements IFormListener

Private WithEvents frm As Access.Form
Private m_bBeforeUpdateTriggered As Boolean

Public Sub Setup(theForm As Access.Form)
    Set frm = theForm
    frm.BeforeUpdate = "[Event Procedure]"
End Sub

Public Property Get BeforeUpdateTriggered() As Boolean: BeforeUpdateTriggered = m_bBeforeUpdateTriggered: End Property

Public Property Let BeforeUpdateTriggered(ByVal bNewValue As Boolean): m_bBeforeUpdateTriggered = bNewValue: End Property

Private Sub frm_BeforeUpdate(Cancel As Integer)
    Me.BeforeUpdateTriggered = True
End Sub

Public Function TimesFieldChanged(theFieldName As String) As Long
    Dim retVal As Long
    retVal = 0
    TimesFieldChanged = 0
End Function

Private Sub IFormListener_Setup(theForm As Access.Form)
    Setup theForm
End Sub

So, the only thing the interface requires is the Private Sub IFormListener_Setup(theForm As Access.Form)

The interface doesn’t care what’s in the routine, just prevents it from compiling if it’s not there. This also will be publicly available and be what is called when I do this in a test module:

' This will only show "Setup" as a method in Intellisense
' When this Setup method is called, it will actually call the "IFormListener_Setup" sub 
Dim NewListener As IFormListener
Set NewListener = New TestFormListener
NewListener.Setup NewForm

Note here that I am defining a NewListener as an IFormListener. The Intellisense in VBA will use the IFormListener methods and show those only, not the other public properties and methods I have added like “TimesFieldChanged” or “BeforeUpdateTriggered”

Alternatively I could Dim NewListener As TestFormListener and then the Intellisense would show the public subs and properties of TestFormListener instead of the Interface public sub.

However, either way, all the public elements that aren’t named the same as in the Interface are available either way. I will test this when I do the next TDD segment though to make sure I am correct and explain the difference a little futher.