In my journey back through the decades, investigating how the software design principles I teach on Codemanship courses could have been applied in programming languages of the day, I’ve visited 2009 (Ruby), 1989 (C), 1979 (Fortran 77) and 1969 (Simula 67), as well as a shiny new language from the present day (Kotlin) to bring us up to date.
For 1999, I’ve thought long and hard about what language to choose, and eventually settled on arguably the most popular at the time: Visual Basic. In that year, VB was in it’s last incarnation before the introduction of .NET. This was the height of the Microsoft COM era. Visual Basic 6 had some elements of object orientation, which were – in reality – built on COM. That is, VB6 “classes” were modules that had private implementations hidden behind public COM interfaces (a class with no fields at all still took up 96 bytes because… COM).
Here’s the carpet quote example done using VB6 class modules.
Room and Carpet are simple data classes, leading to the inevitable Feature Envy in CarpetQuote.
Also, the Quote() function does two distinct jobs: calculating the area of carpet required for a room and calculating the price of that fitted carpet. It knows too much.
Let’s break up the work…
Now let’s move those functions to where they belong.
Plus one for encapsulation, right? Well, not quite. Let’s take a look inside Room and Carpet.
I’d like to hide the data, and get rid of these property procedures. In a language like Java, I could pass the data in to a constructor and keep them private. But VB6 doesn’t support constructors, because COM components don’t support constructors. So I have to instantiate each class and then set their data from outside.
Is there a way to approximate a constructor in VB6 and keep the data hidden? Well, not really. In C, we could use a function in the same module as the data is defined to initialie a new Room or Carpet. VB6 doesn’t support static methods on classes, so a function to create an object could only be defined in a separate module, so the ability to set field values would have to be exposed. We could get rid of our getters, but not our setters.
Then our client can just use the CreateRoom() function to instantiate Room. It’s a little better, but – as with many languages – encapsulation can only really be achieved through discipline in writing client code, not actual language features. (This is just as true in, say, Python as in VB6.)
Now, how about swappability? Does VB6 support easy polymorphism? Remember that, in VB6, “classes” are really modules hidden behind COM interfaces. For sure, we can multiple classes that implement the same COM interface. So, if we wanted to have two different ways of calculating the area of a room, we could define a Room interface (as a .cls file)
…and then have different implementations of Room that know about the details.
CarpetQuote works with either implementation of room, and only sees the Room COM interface with the Area() method. This is how we hide details/data in Visual Basic 6 – using COM interfaces.
So that’s a tick for swappability, and a kind of tick – after a fashion – to encapsulation. Finally, can we make a VB6 class present client-specific interfaces?
Imagine we have a client that just needs to know how many flights of stairs carpet fitters will have to climb/descend to reach a room, so we can calculate a premium. Can we add another interface to an implementation of Room?
A VB6 class can implement more than one COM interface.
Our client binds only to the FloorLevel interface.
So, through the use of COM interfaces, that’s a tick for Interface Segregation.
Which means that – perhaps surprisingly – VB6 is 100% S.O.L.I.D. Who’d have thunk?
You can view the complete source code at https://github.com/jasongorman/SOLID-VB6