Constructors
Home
Introduction
Philosophy
General techniques
Sorting
Searching
Factory
Persistence
Logging
Streaming
Tokenizers
Parsing
File Searching
Command
PseudoPatterns
Compiling
Downloads
FeedBack

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 :

  • Pass the filename when you create an ini file object
  • Pass upperbound to an array-wrapper class.
  • Pass an extension name to a file filter
  • ....

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 :

module modXXX.bas
public function CreateMyClass (byval aParam as astring) as myClass
   dim objResult as myClass
   set objresult = new myclass
   objresult.Initialize aParam
   set CreateMyClass = objresult
end function

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.

Polymorphism

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 :

Interface iSimpleLogger
Sub Log(byval aString as string)

Now, one of the most obvious implementations is a file-logger, another one is a messagebox-logger. The latter implementation could be like this :

class SimpleMsgBoxLogger
implements iSimpleLogger
private sub iSimpleLogger_Log (byval aString as string)
   msgbox aString
end sub

Now a constructor of the SimpleMsgBoxLogger could be :

Public function CreateSimpleMsgBoxLogger as SimpleMessageBoxLogger
   set CreateSimpleMsgBoxLogger = new SimpleMessageBoxLogger
end function

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
   set CreateSimpleMsgBoxLogger = new SimpleMessageBoxLogger
end function

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 ?

Classes become interfaces

The “constructor” approach has another benefit.

Say you have a class clsMyClass that looks like this :

class clsMyClass
public sub DoIt
   debug.print
"Doing it"
end sub

Since you have been paying attention, clsMyClass is PublicNotCreateable and is created via a “constructor” like this :

Public function CreateMyClass as clsMyClass
   set CreateMyClass = new clsMyClass
end function

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 sub DoIt
end sub

private
class clsMyClassImp1
Implements clsMyClass
private sub clsMyClass_DoIt
   debug.print
"Doing it"
end sub

private
class clsMyClassImp2
Implements clsMyClass
private sub clsMyClass_DoIt
   debug.print
"Doing Something similar but different"
end sub
 

Public function CreateMyClass as clsMyClass
   set CreateMyClass = CreateMyClass2(False)
end function

Public function CreateMyClass2 (byval bExtendedMode as Boolean) as clsMyClass
   if bExtendedMode then
       set CreateMyClass = new clsMyClassImp2
   else
       set CreateMyClass = new clsMyClassImp1
   end if
end
function

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.

[GlobalMultiUse]
[
Interfaces Withevents]
[
Let and Set]
[
Cyclic References]
[
Constructors]
[
Singletons]
[
Inheritance]
[
Tasks with progress]
[
.NET Compatibility]
[
Thanks Matthew]
[
Is component registred ?]

 

Site updated : Monday, February 17, 2003