VBA Tips
Should You Use Interfaces in VBA?
Yes, sometimes. And knowing which times is the whole trick.
If you come to VBA from C# or Java, your instinct is to put an interface behind everything. In .NET that instinct is nearly free: the compiler does most of the work, the tooling generates the stubs, and dependency injection frameworks reward you for it. VBA will happily let you follow the same instinct, but it charges you for every interface, and the bill is bigger than it looks. After implementing interfaces across a set of CRUD classes in a production Access application, here is my honest accounting.
What an interface is in VBA
VBA has no Interface keyword. An interface is just an ordinary class module containing empty members:
' Class module: ITransactionRecord
Public Property Get ID() As Long
End Property
Public Property Let ID(ByVal lngNewValue As Long)
End Property
Public Function GetByID(RecordID As Long) As Boolean
End Function
Public Sub Save()
End Sub
A concrete class opts in with the Implements keyword and then must provide every member of the interface, named with the interface as a prefix:
' Class module: clsTransaction
Implements ITransactionRecord
Private Function ITransactionRecord_GetByID(RecordID As Long) As Boolean
ITransactionRecord_GetByID = Me.GetByID(RecordID)
End Function
That second snippet is the part .NET developers underestimate. VBA does not merge the interface into the class's own public face. The class ends up with two parallel sets of members: its own public ones, and the interface-prefixed ones that only surface when you hold the object in a variable declared as the interface type. The standard pattern is to have every interface stub delegate to the matching public member, which means writing, and forever maintaining, a block of boilerplate that roughly doubles the member count of the class.
What interfaces cost you in VBA
Three things, and they compound.
First, the delegation boilerplate itself. Every property needs a prefixed Get and Let pair that forwards to the real one. Every method needs a prefixed wrapper. None of it is generated for you.
Second, double maintenance. Change a method signature and you now change it in the interface, in the class's public member, and in the delegation stub. Miss one and you get a compile error if you are lucky, or subtly divergent behavior if you are not.
Third, no generics and no constructor injection. In .NET, interfaces pay for themselves because the surrounding machinery, generic repositories, DI containers, mocking frameworks, multiplies their value. VBA has none of that machinery. An interface in VBA buys you exactly two things: polymorphism and a test seam. If you are not using either, you bought nothing.
What interfaces buy you
Polymorphism first. I had three CRUD classes, clsTransaction, clsFee, and clsPayment, that were column-for-column identical, differing only in which table they touched. One shared interface, ITransactionRecord, lets a single routine process any of the three through the same object variable. Without the interface, that routine gets written three times or degenerates into a Select Case.
The test seam second, and for me this was the real motive. I write unit tests with Rubberduck, and testing code that talks to a form or a report means substituting a fake for the real repository. You can only substitute a fake where a seam exists, and in VBA the interface is the seam. A class consumed through ITransactionRecord can be handed a test double that implements the same interface and never touches the database. A class consumed directly cannot.
The decision rule
Ask two questions about the class in front of you. Will I write tests that need to fake this class? Will more than one implementation of this shape ever exist, or does identical code want to process several classes uniformly? If either answer is yes, the interface earns its boilerplate. If both answers are no, the interface is ceremony, and ceremony in VBA is not harmless, because every line of boilerplate is a line someone maintains later.
In my case the three transaction classes cleared the bar twice over: identical shapes wanting uniform processing, plus a Rubberduck testing plan. A two-field lookup class in the same application, an ID and a name and nothing else, cleared neither. It will never get a test double that a direct test would not serve equally well, and no second implementation is coming. It gets no interface, and that is a design decision, not a shortcut.
The good news is that this decision is reversible on the cheap. Adding an interface to an existing class later is mechanical: write the empty interface, add Implements, generate the delegation block. Deferring costs you nothing, which means when in doubt, defer.
A note on naming
Name interfaces with a bare I prefix: ITransactionRecord, not clsITransactionRecord. This is the COM convention VBA is built on, IUnknown and IDispatch being the ancestors, and it is what the Rubberduck community expects. The prefix answers "what kind of thing is this" at a glance, and stacking cls in front of I answers it twice while muddying both answers. Let cls mean concrete and instantiable, let I mean abstraction, and declarations start reading correctly on their own: a variable As ITransactionRecord accepts any implementer, New clsTransaction builds the specific one.
The bottom line
Interfaces in VBA are a paid feature. Pay when you need a Rubberduck test seam or genuine polymorphism, and the price is fair. Pay everywhere out of .NET habit and you will drown a small codebase in delegation stubs that exist to satisfy an instinct rather than a requirement. The discipline is not "always use interfaces" or "never use interfaces." It is knowing that in this language, unlike the ones that taught you the habit, every interface has to justify itself.
Rob Evans is an independent Microsoft Access and VBA specialist at Evansco Apps, with fifteen-plus years of professional engineering including C# and .NET. He stabilizes and extends the Access applications that quietly run businesses. rob.evans@evanscoapps.com