Using Object-Oriented Programming with VBScript

With the introduction of the VBScript 5.0 scripting engine, developers now have the ability to create classes in VBScript, much as they can in VB. The next section is intended for those new to object-oriented programming, and provides a quick introduction on the topic. The section following that discusses the VBScript syntax for creating and using classes.

Object-Oriented Programming 101

Object-oriented programming (OOP) is a programming methodology in which entities known as objects are used. Objects contain properties and methods; as the names suggest, a property describes the features of the object, while a method performs some action involving the object.

Tip

True object-oriented programming is further defined as the ability for new objects to inherit properties and methods from existing objects, and for dynamic binding of derived object methods. VBScript’s implementation of OOP doesn’t support these two additional requirements, and is therefore not a true object-oriented programming language.

As an ASP developer, you’ve already used object-oriented code written by others. For example, ADO is nothing more than a collection of objects that can be used to access a database. Having the ability to treat a complex task as a black box is indeed beneficial. When using ADO, you don’t have to worry about what protocol is required to establish a connection to a database; rather, you simply use the Open method of the ADO Connection object.

An object and an instance

To fully understand object-oriented programming, it is essential to understand the difference between an object and an instance of an object.

An object , such as ADO’s Connection object, is an abstraction. It does not physically exist. Rather, it simply serves as a template for creating instances.

An instance is a physical entity of an object. For example, in the following code, ADODB.Connection is an object, while objConn is an instance of the ADODB.Connection object:

'Create an instance of the ADODB.Connection object
Dim objConn
Set objConn = Server.CreateObject("ADODB.Connection")

You must have an instance of an object to make any method calls or to set or get any properties. A real-world example of an object/instance relationship can be seen with automobiles. A “car” is an object, a template describing the features and functionality of an automobile. A 1986 Mercury Sable GS is an instance of a car, able to have its properties modified and its methods implemented.

Encapsulating complexity

Object-oriented programming can be used to encapsulate the complexity associated with particular tasks. For example, imagine that on your web site you needed to be able to open a text file, count the number of lines in the file, and then log that information in another file. Assume you didn’t have an object-oriented approach. Each time you wanted to accomplish this task, you’d need to perform the following steps:

  1. Open a particular text file.

  2. Count the number of lines in that text file.

  3. Close the text file.

  4. Open up the logging file.

  5. Log the number of lines from the file in Step 2.

  6. Close the log file.

Granted, these steps are not complex or too lengthy, but imagine that a novice developer is expected to perform these steps in various ASP pages. We can simplify the process by encapsulating the above steps into a single object. Let’s take a look at creating such an object.

Let’s call this object LogTextFileLines; it will have one property, TextFilePath, which contains the physical path to the log file. LogTextFileLines will also contain one method, UpdateLog, which is responsible for counting the number of lines in a specified text file and recording the line count in the appropriate log file. UpdateLog will have the following definition:

Function UpdateLog(strFileName)

where strFileName is the path to the text file whose lines need to be counted and logged. UpdateLog could return a Boolean value to indicate whether or not the operation was successful.

Once this object is created, the six-step task outlined earlier becomes much simpler:

'Create an instance of the LogTextFileLines object
Dim objLog
Set objLog = New LogTextFileLines

'Set the LogFile path
objLog.LogTextFileLines = "C:\Log\linecount.log"

'Count and log the number of lines in C:\TXT\Scott.txt
If objLog.UpdateLog("C:\TXT\Scott.txt") then
  Response.Write "C:\TXT\Scott.txt was logged successfully!"
Else
  Response.Write "There was an error when attempting to log C:\TXT\Scott.txt"
End if

Being able to treat the entire process like a black box leads to less development time, and most importantly, fewer bugs.

Using Classes in VBScript

If you are not familiar with VBScript classes, but perhaps are familiar with C++’s implementation of classes, you’ll no doubt find VBScript’s use of classes somewhat disappointing. VBScript classes can only contain a single constructor (a constructor is a procedure that’s executed automatically when an instance of the class is created); unfortunately, VBScript’s constructor can accept no parameters. Furthermore, there is no support for inheritance in VBScript.

Tip

Inheritance is an OOP technique used to group objects into a logical hierarchy corresponding to the relationships among objects. For example, if you had a Mammal object, two of its children in this hierarchy might be the Canine and Feline objects. These latter two objects were inherited from Mammal. As an inherited class, Canine and Feline would have the basic methods and properties of a Mammal, as well as their own unique, specialized methods and properties.

When creating classes, keep in mind you are creating a tool to be used by other developers. In the discussion of classes throughout this book, there will be certain times when a distinction between the developer who created the class and the developer who is using the class is needed. The developer who created a class for use by other developers will be referred to as the class developer , while the developer using the created class will be referred to as the end developer .

Since VBScript classes are not compiled into binary objects like COM components, for an end developer to use a class in an ASP script, she must have the class defined within that ASP script. Rather than force the end developers to cut and paste a class definition into each ASP page they use that needs that particular class definition, it is best to create a text file that contains the class definition. Then any end developer who needs to use a class can simply use a server-side include to import the class definition into their ASP page.

As the class developer, once you have created a class, it is important that any changes to the class do not “break” any of the class’s existing methods or properties. For example, if you wanted to add a new feature to an existing class, make sure that when adding it existing code utilizing that class will not break! If you need to add a new feature that changes the existing class’s methods or properties to the point that errors will occur in existing code, you should create an entirely new class that utilizes the new feature. That way, old code still functions.

Creating classes

To create a class, use the Class statement, which has the following syntax:

                  Class 
                  ClassName
  'Define the class's properties and methods here
End Class

where ClassName is the name you choose to assign to your class; your name must follow standard VBScript variable naming conventions.

The Initialize and Terminate events

When creating classes, there are two important event handlers to be aware of: Initialize and Terminate. The Initialize event fires when a class instance is created using the New keyword. For example, we could create an instance of the SomeObject class using:

Dim objSomeObjectInstance
Set objSomeObjectInstance = New SomeObject

The Terminate event occurs when a class instance is freed. A class instance can be freed either explicitly with Set objSomeObjectInstance = Nothing, or implicitly, when it goes out of scope.

We can define these event handlers in our classes. The Initialize event handler can be used to initialize the class’s properties or perform other needed start-up tasks. The Initialize event is often referred to as the class’s constructor . The Terminate event can be used to perform any shutdown tasks, and is commonly referred to as the class’s destructor . Example 4.4 contains the definition of a class with its single constructor and destructor.

Example 4-4. The Initialize and Terminate Event Handlers

Class SomeObject
  Private Sub Class_Initialize(  )
    'This event is called when an instance of the class is instantiated
    'Initialize properties here and perform other start-up tasks
  End Sub

  Private Sub Class_Terminate(  )
    'This event is called when a class instance is destroyed
    'either explicitly (Set objClassInstance = Nothing) or
    'implicitly (it goes out of scope)
  End Sub
End Class

Properties, methods, member variables, and member functions

From the end developer’s standpoint, a class contains properties and methods . Properties are variables that the end developer can set that determine the state of the class. Methods are functions of the class that the end developer can call to have the class perform a given task. For example, the ADODB.Connection object contains properties that describe the state of the object instance, such as ConnectionString, ConnectionTimeout, and Provider, and methods that perform actions, such as Open and Close.

A class, though, can contain variables and functions that the end developer cannot directly call. These “hidden” variables and functions are referred to as member variables and member functions , respectively.

The terminology may seem a bit confusing, or at least overly verbose. To put it another way, a class contains functions and variables. If the end developer can call a class function, the function is referred to as a method; otherwise, it is referred to as a member function. Similarly, if the end developer can call a class variable, it is referred to as a property ; otherwise, it is referred to as a member variable.

The public and private statements

As discussed earlier in Section 4.2.1.2, one of the goals of object-oriented programming is to provide a black box, hiding the implementation details from the end developer. To assist with this encapsulation of complexity, VBScript allows you to hide methods and properties of an object. Remember, an object should serve as a black box for the end developer; as the creator of an object, you may wish to prevent the end user from directly calling specific methods or setting certain properties.

To create member functions and member variables (methods and properties that are hidden from the end developer), precede the member variable or member function definition with the Private keyword. A member function can only be executed from within another one of the class’s methods or member functions. A member variable can only be modified or read by code in a class method, member function, or through a Property Get, Property Let, or Property Set statement (which we’ll discuss shortly).

Tip

Note that the Initialize and Terminate event handlers in Example 4.1 are declared as Private. This prevents the end developer from explicitly triggering these events.

To create a property or method, precede the variable or function definition with the Public keyword. If you don’t explicitly specify whether a function or variable should be public or private, the function or variable will be made public.

The following code creates a class with a public method and property and a private method and property:

Class MyClass
  'Create a public method
  Public Sub A_Public_Method(  )
     'Call the private method
      A_Private_Method
  End Sub

  'Create a public property
  Public A_Public_Property    ' Not a good thing to do


  'Create a private method
  Private Sub A_Private_Method(  )
     '...
  End Sub

  'Create a private property
  Private A_Private_Property
End Class

Note that when using either the Public or Private keywords with variables, you leave off the Dim statement. If you decide not to explicitly specify whether or not a variable is Public or Private, you will need to precede the property name with the Dim statement, which will make the variable Public. However, it is highly recommended that you always explicitly indicate whether or not a variable or function is either Public or Private.

Trying to access private methods or properties through an instance of MyClass will result in an error (see Example 4.5 and Figure 4.2). However, the method A_Public_Method can execute private methods of MyClass; for example, in the previous code snippet, A_Public_Method calls A_Private_Method.

Example 4-5. Accessing Public and Private Methods and Properties

'MyClass definition defined in above code snippet

'Create an instance of MyClass
Dim objMyClass
Set objMyClass = New MyClass

'Since A_Public_Method is Public, this is valid code:
objMyClass.A_Public_Method

'Since A_Public_Property is Public, this is valid code:
objMyClass.A_Public_Property = 7

'Since A_Private_Method is Private, this is code is invalid,
'and will cause an error:
objMyClass.A_Private_Method              ' Invalid

'Since A_Private_Proverty is Private, this is code is invalid,
'and will cause an error:
objMyClass.A_Private_Property = 100        ' Invalid  

'Since A_Private_Proverty is Private, this is code is invalid too
Dim iValue
iValue = objMyClass.A_Private_Property      ' Invalid
An error message generated by Example 4.5

Figure 4-2. An error message generated by Example 4.5

Using Property Get

Since all variables in VBScript are Variants, if we create a public property, we have no control over what type of information the user enters. For example, imagine that we had a class with a property named PhoneNumber. Obviously this variable is intended to store a phone number. However, the developer working with our object could just as easily assign an array to this variable, or an object instance, or a currency value.

In languages like C++, the solution would be to make PhoneNumber a private property and to provide a public method, setPhoneNumber. This public method would expect one parameter: the value of the phone number the end developer wants to assign to PhoneNumber. The setPhoneNumber method would contain code to ensure that the developer couldn’t insert an erroneous or improperly formatted phone number. Since PhoneNumber was made private, another public method, getPhoneNumber, would be needed to allow the developer to read the value of the PhoneNumber property.

An identical method can be implemented using VBScript; however, there is a cleaner way to do this in VB/VBScript. As with the C++ method, start by creating all of your properties as Private. Then if you wish to allow the end developer to read the value of the property, use a Property Get statement in your class. A Property Get statement has the following syntax:

[Public | Private] Property Get PropertyName [(arglist)]
  '... assign the value of the private property to PropertyName ...
End Property

Let’s examine the phone number property we discussed earlier. First things first: we need to create our private property, strPhoneNumber :

Class Information
  'Create a private property to hold the phone number
  Private strPhoneNumber
End Class

Now, if we wanted to allow the developer using our object to be able to read the value of strPhoneNumber, we can add a Property Get like so:

Class Information
  'Create a private property to hold the phone number
  Private strPhoneNumber
  Public Property Get PhoneNumber(  )
    PhoneNumber = strPhoneNumber
  End Property
End Class

The developer can now read the value of strPhoneNumber using the following code:

'Assume the class Information is defined
Dim objInfo
Set objInfo = New Information

'This is a legal way to read the property
Response.Write "Phone Number = " & objInfo.PhoneNumber

'This is illegal, since strPhoneNumber is private
Response.Write "Phone Number = " & objInfo.strPhoneNumber

Often, developers will give their private properties a Hungarian-notation-like prefix (e.g., strPhoneNumber), and will give the corresponding Property Get statements more English-like names. For example, ADO’s Connection object properties have names like ConnectionString and Timeout, not strConnectionString or iTimeout.

Warning

Be sure to give your private properties and Property Get statements different names. If the property and Property Get statement share the same names, you will get a “Name redefined” error.

If the property returned by Property Get is an object, you must use the Set statement when assigning the Property Get name to the private property. For example, if strPhoneNumber was to contain a Dictionary object instead of a string, in the Property Get statement we’d need to use:

Public Property Get PhoneNumber(  )
  Set PhoneNumber = strPhoneNumber
End Property

The arglist component of the Property Get statement should be present when the property requires an argument or index to be properly used. For example, if you had an array as a private property, and wanted to allow the end developer to read a single element from the array as opposed to the entire array, you could use the following code:

Class Information
  'Create an array to hold the US States
  Private aStates(  )

  Private Sub Class_Initialize(  )
    'ReDim the array to hold 50 states, then populate the array
    ReDim aStates(50)
    aStates(0) = "Alabama"
    ' ... and so on ...
  End Sub

  'Create a Property Get statement to grab a certain index from
  'the aStates array
  Public Property Get States(iIndex)
    States = aStates(iIndex)
  End Property
End Class

Once you have an instance of the Information class, use the following code to read a particular value from the aStates array:

'Class Information defined above...

'Create an instance
Dim objInfo
Set objInfo = New Information

'Display the zeroth state
Response.Write objInfo.States(0)

Set objInfo = Nothing

Using Property Let

Property Get allows an end developer to retrieve a private property; Property Let enables the end developer to assign a value to a private property. Property Let statements provide assurances against property corruption. Recall our example at the start of Section 4.2.2.5. If you created a class with a public property named strPhoneNumber, the developer utilizing your class could easily assign anything to strPhoneNumber. If the developer wanted strPhoneNumber to contain “Hey there, Bob,” he could assign this value to the property. If the developer wanted strPhoneNumber to be set to an ADO Recordset object, he could easily assign this value to the property as well.

Of course, you don’t want to allow end developers to enter any kind of information into your class’s properties. Rather, you want to make sure they enter an accepted datatype with an accepted format. strPhoneNumber might require a string datatype with the format ###-###-####. The Property Let statement allows for datatype and format checking when an end developer attempts to assign a value to a property.

Warning

Do not use a Property Let statement for properties of type Object. Rather, use the Property Set statement, which is discussed in detail next in Section 4.2.2.7.

Nearly syntactically identical to the Property Get statement, the Property Let statement has the following format:

[Public | Private] Property Let PropertyName([arglist,] value)
  '... statements: check to see if value is of the correct datatype
  '... and format.  If it is, assign value to the private property
End Property

To assign a value to the private property strPhoneNumber, we could use the following Property Let statement:

Class Information
  'Create a private property to hold the phone number
  Private strPhoneNumber
  Public Property Let PhoneNumber(strPhone)
    'Performs no format or type checking, simply assigns the
    'value passed in by the developer to strPhoneNumber
    strPhoneNumber = strPhone
  End Property
End Class

The above Property Let statement does absolutely no format or type checking on the value the end developer wants to assign to strPhoneNumber. The value parameter (strPhone) of the Property Let statement is the value entered by the end developer to the right of the equals sign. In the following code snippet, “555-4343” would be the value parameter passed to the PhoneNumber Property Let statement:

'Create an instance of the object
Dim objInfo
Set objInfo = New Information

'Assign a value to the phone number
objInfo.PhoneNumber = "555-4343"

Of course, performing no format or type checking is silly, since if we don’t care what types of data are stored in a particular property, then that property doesn’t need to be defined as private. Since we do care about the format of strPhoneNumber, let’s add some format-checking code in our Property Let statement.

When creating format-checking code, you have to decide which formats are acceptable and what to do if the end developer tries to assign an invalid format. For this example, we will accept a telephone format of ###-###-####, and if the end developer enters an invalid format, we’ll raise an error. Example 4.6 contains the new class definition for Information.

Example 4-6. A More Robust Property Let Statement for PhoneNumber

Class Information
  'Create a private property to hold the phone number
  Private strPhoneNumber

  Public Property Let PhoneNumber(strPhone)
    'Ensures that strPhone is in the format ###-###-####
    'If it is not, raise an error
    If IsObject(strPhone) then
      Err.Raise vbObjectError + 1000, "Information Class", _
                 "Invalid format for PhoneNumber.  Must be in ###-###-#### format."
      Exit property
    End If

    Dim objRegExp
    Set objRegExp = New regexp

    objRegExp.Pattern = "^\d{3}-\d{3}-\d{4}$"

    'Make sure the pattern fits
    If objRegExp.Test(strPhone) then
      strPhoneNumber = strPhone
    Else
      Err.Raise vbObjectError + 1000, "Information Class", _
                 "Invalid format for PhoneNumber.  Must be in ###-###-#### format."
    End If    

    Set objRegExp = Nothing
  End Property

  Public Property Get PhoneNumber(  )
    PhoneNumber = strPhoneNumber
  End Property
End Class

If the end developer attempts to execute code such as:

Dim objInfo
Set objInfo = New Information

objInfo.PhoneNumber = "This is an invalid phone number!"

the error message shown in Figure 4.3 will be displayed when viewed through a browser.

Error message produced by an invalid phone number

Figure 4-3. Error message produced by an invalid phone number

Tip

In the Property Get, Property Let, and Property Set statements, the command Exit Property can be used to immediately exit these three statements.

The Property Let statement also has an optional arglist. This arglist must be identical to the arglist in the property’s corresponding Property Get statement, if it exists.

Using Property Set

Classes can have properties that are objects, but special care needs to be taken when returning an object through a Property Get, or when using Property Set to assign an object a reference. Recall from Section 4.2.2.5 that when returning an object reference, it is essential to use the Set keyword. For example:

Public Property Get PropertyName(  )
  Set PropertyName = objPrivatePropertyObject
End Property

VBScript provides a Property Set statement to allow the end developer to assign an object instance to a property. The Property Set statement has the following syntax:

[Public | Private] Property Set PropertyName([arglist,] reference)
  '... Perform any needed checks here, then use
  'Set objPrivateProperty = PropertyName
End Property

The format of Property Set is nearly identical to that of Property Let. The only functional difference between the two is Property Let assigns non-object values to private properties, while Property Set assigns object instances to private properties. For example, imagine we had a class that contained a private property named objConn that was expected to be an ADO Connection object. The class definition, with the Property Set and Property Get statements, might look something like:

Class MyConnectionClass
  'Create a private property to hold our connection object
  Private objConn

  Public Property Get Connection(  )
    Set Connection = objConn
  End Property
  Public Property Set Connection(objConnection)
    'Assign the private property objConn to objConnection
    Set objConn = objConnection
  End Property
End Class

The end developer would use the Property Set statement in the following manner:

'Create an instance of MyConnectionClass
Dim objMyClass, objMyRecordset
Set objMyClass = New MyConnectionClass

Set objConnection = Server.CreateObject("ADODB.Connection")

'Assign objConnection to the Connection property 
Set objMyClass.Connection = objConnection

As with the Property Let statement, the Property Set statement has an optional arglist . This arglist must be identical to the corresponding Property Get’s arglist.

Creating read-only or write-only properties

Using the Property Get and Property Let/Property Set statements enables you to make your private properties editable and readable. However, there is nothing requiring you to use both a Property Get and a Property Let/Property Set for a private property. In fact, you can create read-only and write-only properties by using either just Property Get or just Property Let/Property, respectively.

Tip

Note that while read-only properties are fairly common, write-only properties are not. There are some objects, though, that employ write-only properties, such as the NewMail class of the Collaborative Data Objects for NT Server (CDONTS) object. Write-only properties can lead to headaches for developers using your classes, especially if they are unaware the property is write-only. If the property is write-only, an error will be generated if the end developer attempts to do something like the following:

objClass.WriteOnlyProperty = 5
Response.Write objClass.WriteOnlyProperty

The second line will generate an error, since the value of WriteOnlyProperty cannot be read.

Creating Useful, Reusable Code

In this chapter we discussed the fundamentals of object-oriented programming and looked at how to create classes using VBScript. In the next several chapters, we’ll look at how to tie together the lessons learned in this and the previous chapter to create useful, reusable code. Having a solid understanding of object-oriented programming and the associated VBScript syntax is essential, since the next chapters will use classes extensively.

Get Designing Active Server Pages now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.