I\'m still trying to wrap my head around how Interfaces and Events work together (if at all?) in VBA. I\'m about to build a large application in Microsoft Access, and I want
An interface is, strictly speaking and only in OOP terms, what an object exposes to the outside world (i.e. its callers/"clients").
So you can define an interface in a class module, say ISomething
:
Option Explicit
Public Sub DoSomething()
End Sub
In another class module, say Class1
, you can implement the ISomething
interface:
Option Explicit
Implements ISomething
Private Sub ISomething_DoSomething()
'the actual implementation
End Sub
When you do exactly that, notice how Class1
doesn't expose anything; the only way to access its DoSomething
method is through the ISomething
interface, so the calling code would look like this:
Dim something As ISomething
Set something = New Class1
something.DoSomething
So ISomething
is the interface here, and the code that actually runs is implemented in the body of Class1
. This is one of the fundamental pillars of OOP: polymorphism - because you could very well have a Class2
that implements ISomething
in a wildly different way, yet the caller wouldn't ever need to care at all: the implementation is abstracted behind an interface - and that's a beautiful and refreshing thing to see in VBA code!
There are a number of things to keep in mind though:
Property Get
and a Property Let
(or Set
, depending on the type) for it.Implements
the interface, not the interface itself.That last point is rather annoying. Given Class1
that looks like this:
'@Folder StackOverflowDemo
Public Foo As String
Public Event BeforeDoSomething()
Public Event AfterDoSomething()
Public Sub DoSomething()
End Sub
The implementing class would look like this:
'@Folder StackOverflowDemo
Implements Class1
Private Sub Class1_DoSomething()
'method implementation
End Sub
Private Property Let Class1_Foo(ByVal RHS As String)
'field setter implementation
End Property
Private Property Get Class1_Foo() As String
'field getter implementation
End Property
If it's any easier to visualize, the project looks like this:
So Class1
might define events, but the implementing class has no way of implementing them - that's one sad thing about events and interfaces in VBA, and it stems from the way events work in COM - events themselves are defined in their own "event provider" interface; so a "class interface" can't expose events in COM (as far as I understand it), and therefore in VBA.
So the events must be defined on the implementing class to make any sense:
'@Folder StackOverflowDemo
Implements Class1
Public Event BeforeDoSomething()
Public Event AfterDoSomething()
Private foo As String
Private Sub Class1_DoSomething()
RaiseEvent BeforeDoSomething
'do something
RaiseEvent AfterDoSomething
End Sub
Private Property Let Class1_Foo(ByVal RHS As String)
foo = RHS
End Property
Private Property Get Class1_Foo() As String
Class1_Foo = foo
End Property
If you want to handle the events Class2
raises while running code that implements the Class1
interface, you need a module-level WithEvents
field of type Class2
(the implementation), and a procedure-level object variable of type Class1
(the interface):
'@Folder StackOverflowDemo
Option Explicit
Private WithEvents SomeClass2 As Class2 ' Class2 is a "concrete" implementation
Public Sub Test(ByVal implementation As Class1) 'Class1 is the interface
Set SomeClass2 = implementation ' will not work if the "real type" isn't Class2
foo.DoSomething ' runs whichever implementation of the Class1 interface was supplied
End Sub
Private Sub SomeClass2_AfterDoSomething()
'handle AfterDoSomething event of Class2 implementation
End Sub
Private Sub SomeClass2_BeforeDoSomething()
'handle BeforeDoSomething event of Class2 implementation
End Sub
And so we have Class1
as the interface, Class2
as the implementation, and Class3
as some client code:
...which arguably defeats the purpose of polymorphism, since that class is now coupled with a specific implementation - but then, that's what VBA events do: they are implementation details, inherently coupled with a specific implementation... as far as I know.