VBA is COM-based, and COM doesn't do inheritance. But I think I may have a pattern which approximates some aspects of it:
Animal.cls
Public Sub Speak()
Err.Raise 418, , "Not supported"
End Sub
Dog.cls
Implements Animal
Private mBase As Animal
Private Sub Class_Initialize()
Set mBase = New Animal
End Sub
Private Sub Animal_Speak()
Me.Speak
End Sub
Public Sub Speak()
MsgBox "Woof!"
End Sub
Public Property Get Base() As Animal
Set Base = mBase
End Property
Test.bas
Sub Test()
Dim animal As animal: Set animal = New dog
Dim dog As dog: Set dog = New dog
Dim object As Object: Set object = New dog
dog.Speak ' direct : Woof!
animal.Speak ' polymorphic : Woof!
object.Speak ' late-bound : Woof!
dog.Base.Speak ' fake upcast : Err 418
End Sub
So the interface member Animal_Speak implemented in Dog.cls (which is private to prevent it being called directly) forwards the call to the public Speak method, which can either delegate it to the equivalent sub in Animal.cls (to 'inherit') or replace it with something else (to 'override').
Also, the Base property in Dog.cls is exposed in case the caller does actually want the base behaviour (to 'upcast').
(terms in quotes because they're approximations only).
So, my questions are:
Implementation Inheritance - Not supported in VBA. This is the ability to inherit method implementation from a base class in a derived class. VBA does not implement derived classes, it exposes the methods that need to be implemented. VBA does not implement interfaces, it exposes the methods that need to be implemented.
Visual Basic for Applications (VBA) is an event-driven, object-oriented programming language for writing macros, used for the entire Office suite as well as other applications....
The pattern you're implementing is a form of composition, which is a commonly used surrogate for inheritance, even in languages that support class inheritance. You see inheritance has its downsides, and composition is often preferred over it.
Composition doesn't require interfaces. In fact, whenever you encapsulate an instance of an object in a class, and then expose some (or all) of the inner object's members, you are using composition.
In your specific example, you're composing with an "abstract class" (Animal
- the interface), which doesn't make much sense, since an interface isn't meant to be instantiated directly like this:
Set mBase = New Animal
In real code, you could have an IRepository
interface, a ProductSqlRepository
that implements it (then a SupplierSqlRepository
and then an OrderSqlRepository
, etc.), and you could compose the implementations with some SqlRepository
class that exposes the common functionality that all implementations use, each in their own specific ways: meanwhile the client code only ever needs to know/care about IRepository
.
You're discovering the possibilities of polymorphism in VBA, by means of interface inheritance, which is not the same as class inheritance.
With class inheritance, you can have virtual
and even abstract
methods to override
in derived classes.
With .NET-flavored interface inheritance, you can have an interface that extends another interface, and an implementing type can implement just that interface to satisfy the compiler - it exposes the members of all interfaces it's extending.
With VBA-flavored interface inheritance, you get a class that can implement an interface. Or two. Or three. Or more. ...the way COM types do.
And that's.. quite awesome already.
It's called Object Oriented Programming - OOP sits on 4 things:
It's a prevalent paradigm in quite a lot of programming languages, such as Java and C#. The ideal OOP code is SOLID, loosely-coupled code that can easily be unit-tested. SOLID principles guide a lot of OOP designs:
Despite the lack of inheritance capabilities, VBA can still respect these OOP principles, while embracing its constructorless COM nature. Extensible Logging on Code Review, as well as the follow-up post that implements a DatabaseLogger, make a pretty powerful demonstration of it.
But even without using interfaces, thinking in OOP makes it possible to encapsulate any functionality, and write it in such a way that it's a reusable component. Like this reusable progress indicator that illustrates how you step away from a UI (UserForm, or worksheet) code-behind that runs the show, to getting the UI to be nothing more than an I/O device in your application logic.
By learning OOP with VBA, you're sculpting your thought process and embarking on a journey from a procedural paradigm, to the wonders of object-oriented code. Once you've mastered this, you'll want to expand your experience to full-fledged class inheritance, and discover delegates and anonymous functions, and heck perhaps even look into the Functional Programming paradigm, which is yet another completely different way of thinking about code, like OOP is to procedural.
The VBA IDE, the glorious VBE, unfortunately was last updated back when VB6 was all the rage, and doesn't have much OOP-encouraging features. One could even say that the VBE actively hates OOP:
To be fair, unit testing and refactoring tools weren't as widespread in 1999 as they are today (AFAIK). Still, the downside of OOP in VBA is the lack of features in the IDE itself.
Fortunately the VBIDE has an extensibility model and supports add-ins. So you can get Rubberduck and have all these features and write OOP code in VBA without constantly raging against the lack of IDE features.
DISCLAIMER: I manage the Rubberduck open-source project, hosted on GitHub.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With