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 (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.
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.
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:
Open a particular text file.
Count the number of lines in that text file.
Close the text file.
Open up the logging file.
Log the number of lines from the file in Step 2.
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.
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.
To create a class, use the
Class
statement, which has the following
syntax:
Class
ClassName
'Define the class's properties and methods hereEnd Class
where ClassName
is the name you choose to
assign to your class; your name must follow standard VBScript
variable naming conventions.
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
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.
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
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 GetPropertyName
[(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 strPhoneNumberPublic 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
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 LetPropertyName
([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 strPhoneNumberPublic Property Let PhoneNumber(strPhone)
'Performs no format or type checking, simply assigns the 'value passed in by the developer to strPhoneNumberstrPhoneNumber = 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 errorIf 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 fitsIf 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.
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.
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 SetPropertyName
([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 PropertyPublic Property Set Connection(objConnection)
'Assign the private property objConn to objConnectionSet 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
.
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.
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.