Object Oriented VBA: Polymorphism


Polymorphism – the ability to assume more than one form.

Q: When can a Cat also be a Dog?

A: When it’s a polymorphic Fish.

In a Standard Module define this procedure:

Public Sub DemoPolymorphism()

Dim Pussy As Cat: Set Pussy = New Fish
Dim Growler As Dog: Set Growler = New Fish
Dim Billy As Fish: Set Billy = New Fish
Dim Puss As Cat: Set Puss = Billy

Pussy.MakeSomeNoise
Growler.MakeSomeNoise
Billy.MakeSomeNoise
Puss.MakeSomeNoise

End Sub

Here we have created 3 instances of the object defined by the class “Fish” on the Heap.  However, the pointers (references) to each instance are of 3 different types (Cat, Dog and Fish).  Finally, we create a 4th reference which points to the 3rd instance and this really illustrates polymorphism: the same object appears as either a Fish (Billy) or a Cat (Puss).

Microsoft’s Visual Basic for Applications implements Polymorphism using Interfaces.  This is a PITA as it means that you have to define the implementation for every class that inherits from the Interface.  Depending upon the number of Properties and Methods in the Interface (a Sub procedure is a Method that returns no value, a Function is a Method that returns a value), this can lead to a lot of duplicated code.  This is unavoidable.  In Visual Basic .Net you can inherit from Interfaces and Classes, but in VBA you don’t have this choice.  The glass is half empty.  The upside is that the number of Interfaces a class can inherit is unlimited.  In our Fish class we inherited two interfaces: Cat and Dog.

For completeness, here are the interface definitions for Cat and Dog:

The Class Module “Cat” is defined simply as:

Public Sub MakeSomeNoise(): End Sub

and the Class Module “Dog” is defined identically:

Public Sub MakeSomeNoise(): End Sub

Note: it is common to distinguish an Interface from a Class in VBA by prefixing an Interface’s name with “I”, eg. ICat and IDog. I have not done this in the above example because I think it detracts from the paradigm. Prefixing with “I” is a good way of distinguishing class modules but it’s not necessary. What is necessary is the use of the reserved word “Implements” in our Class module as we will now see with in the definition of the Fish class:

Implements Cat
Implements Dog

Private Sub Cat_MakeSomeNoise()
Debug.Print "Meow"
End Sub

Private Sub Dog_MakeSomeNoise()
Debug.Print "Grrr"
End Sub

Public Sub MakeSomeNoise()
Debug.Print "Pfft"
End Sub

Note the implementation of the Interface methods for Cat and Dog: they contain the “_” character as a separator between the Interface’s name and the Interface’s method.  The final method (Public Sub MakeSomeNoise) is a method particular to the Fish class.  It is not part of either Cat or Dog so it does not appear as part of IntelliSense when looking at the methods of an object referenced by Cat or Dog.

This is just one way of looking at polymorphism: a different view (pointer/reference) on the same object.  Another way is to look at the same view on different objects.

Public Sub DemoPolymorphismWithSpellingErrors()
Dim Canine As Dog, i As Integer
For i = 1 To 3
Set Canine = GetDog(i)
Canine.MakeSomeNoise
Next i
End Sub

Private Function GetDog(i As Integer) As Dog
Select Case i
Case 1: Set GetDog = New Alsatian
Case 2: Set GetDog = New BullMastif
Case 3: Set GetDog = New Doberman
End Select
End Function

The definitions of our Alsatian, BullMastif and Doberman classes are as follows.

Alsatian:

Implements Dog

Private Sub Dog_MakeSomeNoise()
Debug.Print "German Sheperd"
End Sub

BullMastif:

Implements Dog

Private Sub Dog_MakeSomeNoise()
Debug.Print "BullMassive"
End Sub

Doberman:

Implements Dog

Private Sub Dog_MakeSomeNoise()
Debug.Print "Dobernam"
End Sub

In this example notice that I’ve used a function that sets and returns (to the caller) the type of Dog I want. This is just good development practise: keep your code “atomic” – a method should do one thing only and do it well. When you need to change your code in the future you will only need to do it in one place.

Again, I’ve had to define the implementation of the Dog interface methods in each of the 3 classes (Alsatian, BullMastif and Doberman). Again this is a PITA “feature” of object-oriented (“OO”) programming in Visual Basic for Applications.’

And that’s about all there is to Polymorphism in VBA. The real skill is in identifying areas where it is appropriate to use it. VBA doesn’t have the concept of ‘Reflection’ like true OO languages such as VB.Net so implementing it like I have with a SELECT..CASE statement is another PITA. it’s a shame you can’t do something like:

Dim str as String: str = "Alsatian"
Set Canine = New str

That would save A LOT of lines of code where you have many classes that conform to a single interface like Dog. However, VBA is more of a half-way house when it comes to OO and there are many techniques available in VB.Net that aren’t available in VBA. Hopefully I have made the above examples clear and given you the confidence to use polymorphism in VBA. Now it’s up to you: go out there and live the dream.

Categories:VBA

4 comments

  1. It’s a PITA because you’re doing it wrong. Okay, so it’s still a PITA, but can be made substantially easier. A `GermanShepard` should by all rights also have a public function `MakeNoise` that returns the correct value. *All* of the private `Dog_MakeNoise` implementations for all of the classes be identical so that it can call the “concrete” public implementation with `Me.MakeNoise`. This allows us to copy pasta all of the private interface implementations verbatim into all of our implementing classes. Still sucks, but quite a bit easier than the way you’ve shown here. No offense meant though. Great write up and I’m happy to see someone else using OOP in their VBA.

  2. Aw, this was a really nice post. Taking a few minutes and actual
    effort to produce a very good article… but what can I say… I put things
    off a whole lot and don’t manage to get nearly anything done.

Leave a Reply to p1ngp1ng Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: