A constructor is a special class method that is meant to create an object in an initialized state. VB doesn’t have “true” constructors. VB has the Class_Initialize method which allows you to initialize certain members of the new object. Unfortunately, in many circumstances the “simple” scheme of CLass_Initialize is not enough.
More often than not, you want to pass some parameters to the initialization process. It makes sense to :
Suppose we have a class myClass that we wish to create only with a certain pre-initiliazed string variable as a parameter.
There are two ways to solve this problem.
The first one involves creating an object model in which lower level objects can only be accessed or created by navigating higher objects in the object model. This is a good technique but is a little involved. Anyway the real solution is more or less the same as the second method.
The second method involves turning myClass into a PublicNotCreateable class. This prevents any user to try to “new” myClass from outside the component. It is now the component’s responsibility to provide a means to instantiate myClass. The simplest way to do this to provide a public global method like this :
It is clear that modXXX.bas must reside inside the same component as myClass. (If not, you could not do a ‘new’ of myClass since it is Public Not Createable). The CreateMyClass function acts as a kind of constructor for myClass. The Initialize method on myClass is a proposed name for a method on myClass. Since we don’t want this initialize method to be visible outside of the component, I make it a Friend method.
Nothing prevents you from having multiple possible ‘constructors’ and multiple friend “Initialize” methods on myClass.
The only thing left to do is make sure the CreateMyClass method is calleable from outside the component. This is shown in a related article about GMU classes.
Even purists will notice the similarity between this technique and real constructors such as those in Java, C++ or Dephi. The proposed technique works just as well but has the disadvantage or relying on conventions. The encapsulation could have been better.
To show you that the technique is not as bad as one thinks, I’ll given two slightly advanced uses of “constructors” which are harder to do in other languages.
The previous example is good for the general case. Suppose now that you have designed a set of related implementations for a given interface. A Good exampe would be the logging classes. These classes could become totally irrelevant. The only way in which they differ is the way they are created. The rest of their lifetime is spent as some predefined interface.
I’ll recap the example from the logging component. It introduces the very simple concept of a logcontext. This is something which “takes a string and logs it”. In interface langauge this would look like :
Now, one of the most obvious implementations is a file-logger, another one is a messagebox-logger. The latter implementation could be like this :
Now a constructor of the SimpleMsgBoxLogger could be :
function CreateSimpleMsgBoxLogger as SimpleMessageBoxLogger
If understood the gibberish I was saying at the start of this sub-section, you realize that the code should in fact be :
Public function CreateSimpleMsgBoxLogger as iSimpleLogger
The code difference might be subtle but is very important. It allows the class SimpleMessageBoxLogger to stay private in our component. The client doesn’t know or care which class he has just instantiated. He just needs an iSimpleLogger that logs to a messagebox and doesn’t care about all the rest. This is a kind of encapsulation that IMHO makes good components different from others. It is not always possible to completely hide the class but if you can design it like this it makes you feel very good. As far as the user is concerned you can change just about anything about SImpleMessageBoxLogger. Since the class is private, binary compatibility doesn’t come into play.
Of course, other language can achieve the same functionality. However, they have to introduce factory methods (like our “constructors”). They now end up with constructors and factory methods while VB has only one concept : “factory methods”. Now which language is cleanest ?
The “constructor” approach has another benefit.
Say you have a class clsMyClass that looks like this :
Since you have been paying attention, clsMyClass is PublicNotCreateable and is created via a “constructor” like this :
Public function CreateMyClass as clsMyClass
Now suppose that at some points in your application you really would like clsMyClass to do something similar. The object would look the same and have the same methods but some methods would perform differently. Here we can actually take advantage of the similarity between classes and interfaces in VB. We could change our code like this :
PNC class clsMyClass
Public function CreateMyClass as clsMyClass
The nice thing is that we now have a new alternate constructor. Instead of pushing the mode into clsMyClass, we have created two different implementations. clsMyClass once contained implementation code but is now a pure interface. The two new classes clsMyClassImpX can be private inside the component. Existing code will continue to work but new code can exploit the alternate “constructor”.
This feature is quite unique in VB.
|Site updated : Monday, February 17, 2003|