An object is constructed based on its class type, or the class to which it conforms. It's good programming practice to always construct an object with a valid state, meaning the object contains all the information required to make it accessible to the application. Subsequent invocations might be futile if the object is not in a state in which it can positively respond. For instance, assume you want to open a savings account (the object) in a bank (the application). In order for the bank representative to successfully create the account, he or she is required to provide a valid bank account number, your name, address, and social security number to your account. If the account is opened without all the required information, it might become a nuisance to the computer system each time it tries to locate the account information, thereby bringing grief to both the bank and to you, the customer. For example, if the bank representative opened an account for you without a bank account number, how would the bank know where to put your deposits? You would, I'm sure, take your business elsewhere. The bank would also go out of business eventually, if it continued to run its operation in that manner. This scenario begs the question, "How can an object be constructed in an initially valid state?"
Classes have special methods that are called constructors in object-oriented terminology. A constructor is called during the construction process of an object. This is your opportunity to provide implementation code that initializes the state of the object during construction. If you do not explicitly use a specific constructor at object construction time, the default constructor is called. The default constructor accepts no parameters. If you do not define a default constructor in a class, the compiler will provide one that does nothing.
Most object-oriented languages permit classes to define multiple constructors. This gives the programmer the ability to construct an object of a given class in various valid states, depending on the situation. With the exception of the default constructor, a constructor can be defined to accept parameters. This will allow an object to be constructed in a valid state, that state being dependent on external factors that are passed at construction time. Similar to Visual Basic Sub procedures, constructors do not return anything.
Visual Basic supports only a default class constructor. To define a default constructor, open a class module in a Visual Basic project, and then select Class from the Object drop-down list in the code module editor. In the Procedure drop-down list, you'll find two event items: Initialize and Terminate. Selecting the Initialize event will create the event handler shown in Figure 2-5.
Figure 2-5. Visual Basic uses the Initialize event as a constructor.
The subroutine Class_Initialize is the default constructor that is invoked when a Visual Basic class object is constructed. In the body of this subroutine, you can initialize private class members that represent the state of the object. Objects are constructed by using either the keyword New or the function CreateObject. If you declare an object variable and then try to use that object variable before you have used New or CreateObject to construct it, no compile-time errors will occur. However, at run time you will get an error because object variables are not objects, but rather are references to objects. Object variables by default are null, which is represented in Visual Basic by the keyword Nothing. In the following code extract, I've declared a SavingsAccount object variable, and then I've attempted to deposit $10 into the savings account. This code will compile, but at run time an error will be raised when the attempt to invoke the Deposit method occurs because the mySavAcct object variable refers to Nothing.
Dim mySavAcct As SavingsAccount Dim nBalance As Long ' Compile time: OK ' Run time: Fail - mySavAcct refers to Nothing nBalance = mySavAcct.Deposit(10)
This problem can be corrected by constructing an object in one of three ways: implicitly, using the keyword New; explicitly, using the keyword New; or explicitly, using the CreateObject function. The following sections take a closer look at each of these three methods.
By declaring an object variable as New followed by the type (class), Visual Basic will implicitly construct an object of the requested type and return a reference to the object variable upon the first invocation attempt. Following is a code extract in which I have modified the declaration of the mySavAcct object variable from the previous example to include the keyword New. This modification will automatically create an object and assign a reference to the object to the mySavAcct object variable when the first invocation attempt is made:
Dim mySavAcct As New SavingsAccount Dim nBalance As Long ' Compile time:OK ' Run time: OK _ SavingsAccount object is auto-constructed and ' the reference is returned to mySavAcct. ' mySavAcct invokes the Deposit method of the ' SavingsAccount object that it references. nBalance = mySavAcct.Deposit(10)
This approach will produce successful results every time. You never have to be concerned about an object variable that you declare referring to Nothing, because every time the object variable is invoked, Visual Basic checks whether it's set to Nothing. If the object variable is set to Nothing, Visual Basic constructs it. Despite this benefit of knowing the object variable will always reference something, I suggest you avoid using this method for a couple of reasons. First, you have to pay the price of overhead for every invocation performed on an object variable declared in this fashion because of the checking and constructing Visual Basic has to do. In the following code extract, three actions are performed on the SavingsAccount object referred to by the mySavAcct object variable. The comment above each invocation is an indication of what Visual Basic does that causes overhead:
Dim mySavAcct As New SavingsAccount Dim nBalance As Double Dim nInterestEarned As Double ' If mySavAcct Is Nothing Then ' Set mySavAcct = New SavingsAccount ' End If nBalance = mySavAcct.Deposit(10) ' Deposit cash. ' If mySavAcct Is Nothing Then ' Set mySavAcct = New SavingsAccount ' End If nInterestEarned = mySavAcct.IE ' Check interest earned. ' If mySavAcct Is Nothing Then ' Set mySavAcct = New SavingsAccount ' End If mySavAcct.Owner.LName = "Jones" ' Update account owner's last ' name to Jones.
A second important reason to avoid declaring an object variable as New is that it is impossible to check for the Nothing condition. Imagine a client that has an object variable that maintains a reference to a component object across machine boundaries. Accessing an object's properties or methods across boundaries to determine what action to take requires a lot of excess overhead if the choice of actions can be accomplished by checking for the Nothing condition. Checking for Nothing does not require the internals of Visual Basic to venture out of the client process.
To construct an object explicitly, first declare an object variable of a specific type. Then, in the body of the implementation, create a class object that represents the type declared by the object variable and return a reference to it. The syntax for doing so is as follows:
Dim mySavAcct As SavingsAccount ' Explicitly construct SavingsAccount object and return ' a reference to the object variable mySavAcct. Set mySavAcct = New SavingsAccount ' OK at run time nBalance = mySavAcct.Deposit(10)
Note that in an explicit construction, object variables are preceded with the keyword Set. Visual Basic requires that any time an object variable is being assigned a reference, it is preceded with Set. Although this syntax might seem awkward, it grows on you. Unlike the implicit construction approach, there is no additional overhead incurred because the compiler lets the programmer ensure that an object variable references a valid object. If not, the compiler will simply raise a run-time error.
A third alternative for constructing a Visual Basic object is to utilize the CreateObject function instead of the keyword New. CreateObject might be your only option if the object you want to construct resides in an ActiveX component that does not provide a type library. Consequently, you cannot declare an object variable of a specific type. You are forced to declare an object variable of either Object or Variant type. These days, it is extremely rare that a component does not have a type library. Keep in mind that you can still use CreateObject even with a type library.
CreateObject uses the COM object creation services for constructing COM objects. This implies that the Visual Basic programmer must have an understanding of COM in order to appreciate why CreateObject might be a desirable alternative over the keyword New. The CreateObject function creates a COM object and returns a reference to that object to an object variable by using a Set statement syntax similar to explicit construction using the keyword New. The syntax is as follows:
Set myObject = CreateObject("ActiveXComponent.Class")
ActiveXComponent is the name of the component that contains the desired class. Class is the name of the class from which the object is constructed. In COM terminology, this is referred to as a programmatic identifier (progID). When an ActiveX component is registered in Microsoft Windows, it adds a progID for each class defined in the component to the Windows Registry. (The Windows Registry is a system database that COM uses to manage ActiveX components.) You can think of the progID as the key in the database that allows COM to quickly locate ActiveX components and create instances of the classes requested by the CreateObject function.
The keyword New uses COM object creation services only when New is creating a COM object that is external to the project of which the object is a part. Therefore, the keyword New is faster for the creation of objects internal to a given project, because it has some private creation processes that are optimized for internal object creation. I will discuss in greater detail the subtleties of using the keyword New vs. the CreateObject function in Chapter 3, "COM Threading Models."
Explicit construction using the keyword New is the ideal way to create objects in Visual Basic for most situations. Use CreateObject only if you have no choice, such as in the case of a missing type library or when your intention is to take advantage of a COM feature that will otherwise be impossible. Avoid the implicit construction method at all costs—it is a hindrance because of the overhead incurred with every invocation, and because of the inability of the programmer to control the construction process of the class object referenced by an object variable.
No matter which of the three approaches you use, an object's default constructor is called, and this constructor is your only opportunity in Visual Basic to ensure that a class object is constructed in a valid state.
A well-behaved object should not only maintain a valid state while in existence, but it should also clean up after itself before it is destroyed. If encapsulation is exercised properly, only the object knows what parts of its representation need special attention before it destructs. Throughout Part Two of this book, you will find numerous design patterns that illustrate how different types of objects collaborate with one another to provide a solution to solve a specific problem domain. For design patterns to be successful, all objects of the pattern must play nicely together. If an object in a design pattern relationship is destroyed without regard to other dependent objects in the pattern, failure is imminent.
Classes define a special method known as a destructor. When a class object is in the process of being destroyed, it calls its destructor. The destructor method takes no parameters and returns nothing. A class can define only one destructor. This destructor method gives a class object an opportunity to do whatever house cleaning is necessary, such as notifying dependent objects of its departure. In general, a destructor should not be made accessible to clients of a class object, because calling a destructor should nullify the representation of an object, thus making its state invalid.
Visual Basic supports the concept of a destructor in the form of a Terminate event handler defined in a class module. In the Object drop-down list of the class module editor, select Class. Notice that the Procedure drop-down list contains two events: Initialize (which I've mentioned previously) and Terminate. When you select Terminate, Visual Basic automatically creates the following event handler skeleton, which it will call when an object of this particular class is destroyed. Put your cleanup code within the body of this method:
Private Sub Class_Terminate() End Sub
Visual Basic objects cannot be destroyed explicitly. An object is destroyed when all references to it are released. As mentioned, object variables are not objects; rather, they hold on to references of objects. Therefore, the life of an object is controlled implicitly by object variables.