What’s the big deal with folders?

If you’re a seasoned VBA developer, you probably have your “VBA Toolbox” – a set of classes that you systematically include in pretty much every single new VBA project.

Before you’ve even written a single line of code, your project might look something like this already:

ProjectExplorer

The VBE’s Project Explorer gives you folders that regroup components by component type: forms in a folder, standard modules in another, and classes in another.

That’s great ok for small projects, perhaps. But as your toolbox grows, the classes that are specific to your project get drowned in a sea of class modules, and if you’re into object-oriented programming, you soon end up finding needles in a haystack. “But they’re sorted alphabetically, it’s not that bad!” – sure. And then you come up with all kinds of prefixes and naming schemes to keep related things together, to trick the sorting and actually manage to find things in a decent manner.

The truth is, this sucks.

Now picture this:

You shouldn’t have to care about a component’s type. Since forever, the VBE has forced VBA developers to accept that it could possibly make sense to have completely unrelated forms grouped together, and completely unrelated classes together, just because they’re the same type of VBA objects.

But it doesn’t have to be this way. I like how Visual Studio /.net lets you organize things in folders, which [can] define namespaces, which are a scope on their own.

There’s not really a concept of namespaces inside a VBA project: you could simulate them with standard modules that expose global objects that regroup functionality, but still every component in a project is accessible from anywhere in that project anyway. And who needs namespaces anyway?

Rubberduck 2.0 will not give you namespaces – that would be defying the VBA compiler (you can’t have two classes with the same name in the same project). What it will give you though, is folders.

Cool! How does it work?

Since the early versions of Rubberduck, we’ve been using @annotations (aka “magic comments”) in the unit testing feature, to identify test modules and test methods. Rubberduck 2.0 simply expands on this, so you can annotate a module like this:

'@Folder Foo.Bar

And that module will be shown under a “Bar” folder, itself under a “Foo” folder. How simple is that! We’ll eventually make the delimiter configurable too, so if you prefer this:

'@Folder Foo/Bar

Then you can have it!

With the ability to easily organize your modules into a “virtual folder hierarchy”, adhering to the Single Responsibility Principle and writing object-oriented code that implies many small specialized classes, is no longer going to clutter up your IDE… now be kind, give your colleagues a link to Rubberduck’s website 😉


The Code Explorer

The goal behind the Code Explorer feature, is to make it easier to navigate your VBA project. It’s not just about organizing your code with folders: since version 1.21, the Code Explorer has allowed VBA devs to drill down and explore a module’s members without even looking at the code:

CodeExplorer-expanded

The 2.0 Code Explorer is still under development – but it’s coming along nicely. If you’re using the latest release (v1.4.3), you’ll notice that this treeview has something different: next release every dockable toolwindow will be an embedded WPF/XAML user control, which means better layouts, and an altogether nicer-looking UI. This change isn’t being done just because it’s prettier: it was pretty much required, due to massive architectural changes.

Stay tuned!

Advertisements

OOP in VBA: Immutability & The Factory Pattern

If you’ve ever exported a VBA class module and opened it in Notepad, you’ve probably already seen this:
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 ‘True
END
Attribute VB_Name = “Class1”
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False

These attributes unlock the true OOP potential of VBA.

VB_PredeclaredId

You know how every UserForm comes with a “default instance” for free? That’s because user forms have this attribute set to True, and that instructs VBA to create a global-scope object named after the type, so you can do this:

MyForm.Show vbModal

Without even creating an instance of MyForm. That’s not very OOP though – in fact it’s pretty much anti-OOP, since by doing that you’re not creating any objects… So what’s the use of this attribute in an OOP discussion? Keep reading.

VBA classes are Public, not creatable. This means when you reference a VBA project, you can see and use the classes in that project, but you can’t create instances of them. You need a way to expose functionality to the referencing VBA project, to return instances of such a class. Oh sure, you could add a standard module and expose a public function that does it – but standard modules don’t quite encapsulate their members, and it’s up to the client code to properly qualify function calls (e.g. FactoryModule.CreateMyClass). A better option is to create a dedicated object whose sole responsibility is to create objects of a given type – enter the factory patern.

In OOP design patterns, factories are often combined with the Singleton pattern – after all, there only ever needs to be one single instance of a factory class. Given that the class can’t be created by the client code with the New keyword, that’s precisely what setting the VB_PredeclaredId attribute to True will do.

Example

Say you have a Car class, with Make, Model and Manufacturer properties. It wouldn’t make sense for any of these properties to be changed after they’re set, right?

Private Type TCar
    Make As Integer
    Model As String
    Manufacturer As String
End Type
 
Private this As TCar
Public Property Get Model() As String
    Model = this.Model
End Property
 
Friend Property Let Model(ByVal value As String)
    this.Model = value
End Property
 
'...other properties

 

The Friend access modifier makes the Model property immutable, because client code located outside the VBA project this Car class is defined in, simply won’t see the Property Let member. However a CarFactory class defined in the same VBA project can:

 Public Function Create(ByVal carMake As Integer, ByVal carModel As String, ByVal carManufacturer As String) As Car
     Dim result As New Car
     result.Make = carMake
     result.Model = carModel
     result.Manufacturer = carManufacturer
     Set Create = result
 End Function

Because this CarFactory class has a PredeclaredId, the referencing VBA code can do this:

 Dim myCar As Car
 Set myCar = CarFactory.Create(2016, "Civic", "Honda")

And then the myCar object can’t be turned into a 2014 Honda Fit – not even by accident.

Future versions of Rubberduck will make it easy to set a class’ PredeclaredId attribute, and might actually provide tools to automate the creation of factories.