The Visual FoxPro Object Model
The Visual Foxpro object model is based on a strong foundation of object-oriented principles. User defined classes, protected class members, inheritance, - everything you would expect to find in a robust object model. The model includes a set of built-in classes that you use as a basis for creating your own classes. These built-in classes are called base classes.
Base classes are grouped into controls and containers. The difference is that a container can include other controls or even other containers within itself, while a control cannot. (The “control” control is somewhat of an exception to this rule, since it allows us to refer to multiple controls as if they were one). A good example of a container is a form that contains a group of text boxes. The form is referred to as the Parent, while the text boxes are referred to as children (or child controls).
Here is the current list of base classes:
Base class properties
An object has properties used to describe the object, or to hold values that represent the object’s state. All Visual Foxpro base classes share a common minimum set of properties:
|Class||The name of the class of the object|
|BaseClass||The name of the base class of the object|
|ClassLibrary||The full path of the class library where this class is defined|
|ParentClass||The name of the class of the parent class of the object|
You are free to add custom properties to any new class you create. I’ll show you how in just a moment.
Base class events and event methods
Most objects also exhibit some kind of behavior in the form of methods. In Visual Foxpro, object can also respond to events that occur in the system. For example, when the user clicks on a command button, Visual Foxpro invokes the Click event. You can place code in a command button’s Click() event method that will automatically run whenever the button is clicked.
All Visual Foxpro base classes can respond to a common minimum set of events:
|Init||Invoked when the object is created|
|Destroy||Invoked when the object is released|
|Error||Invoked when an error occurs inside one of the object’s methods|
Class definition syntax
While it’s possible and probably more desirable to create all your classes, including non-visual ones, using the Class Designer, it’s easier and more straightforward in a chapter of this nature to demonstrate the features of the language in code. The code that follows can be typed directly into a PRG. Just be sure to place the CREATEOBJECT() function calls before any class definitions!
When creating new classes, we use the new DEFINE CLASS command:
DEFINE CLASS MaintenanceForm AS Form ENDDEFINE DEFINE CLASS Transaction AS Custom ENDDEFINEThese statements create new classes based on Visual Foxpro’s built-in classes. More precisely, we are subclassing a Visual Foxpro base class. Through the power of inheritance, we automatically inherit any properties and methods defined in the class that we are subclassing, and thus can treat them as if they were actual members of our new classes:
*-- Create an instance of the Transaction class and print the *-- value of the Class property. oCreditCardTransaction = CREATEOBJECT(“Transaction”) ? oCreditCardTransaction.Class && Prints “Transaction” on screenLet’s modify our transaction class definition to show how the Init() and Destroy() event methods work:
DEFINE CLASS Transaction AS Custom FUNCTION Init() WAIT WINDOW “Creating object” ENDFUNC FUNCTION Destroy() WAIT WINDOW “Destroying object” ENDFUNC ENDDEFINEThe Init event is invoked by Visual Foxpro whenever an object is created. The Init event causes code defined in the Init() event method to be executed. The generic object-oriented term for this type of event method is called a constructor. It is commonly used to initialize properties of the object, or to ensure the environment is correctly setup before the object is used.
The Destroy event is invoked by Visual Foxpro whenever an object is destroyed. The Destroy event causes code defined in the Destroy() event method to be executed. The generic object-oriented term for this type of event method is called a destructor. It is commonly used to clean up the environment when an object is being released.Note If you looked carefully, you might have noticed the empty parenthesis after the method name. This is not an error! In fact, instead of using the PARAMETERS statement, (or more accurately - LPARAMETERS) you can now define parameters within parenthesis immediately after the method name. The empty parenthesis are a matter of coding style, and are strictly optional.
As you may have already guessed, the syntax for creating an instance of a class, is:
*-- object in memory
oTransaction2 = oTransaction
oObjectReference = CREATEOBJECT(cClassName)
The CREATEOBJECT() function accepts a class name as a parameter, and returns a reference to an object. This object reference is really just a memory variable with a data type of “O”, which stands for object. Variables that represent objects function are very similar to other types of variables. For example, it is perfectly legal, although not good programming practice, to assign a value of a different data type to a variable that represents an object:
oTransaction = CREATEOBJECT(“Transaction”)
oTransaction = “A line of text” &&; an object is just a variable
Note that when passing objects as parameters to a function or method, the object is always passed by reference, never by value. Also, when assigning an object to another memory variable, the new variable is a reference to the same object!
*-- Both oTransaction2 and oTransaction refer to the same
*-- object in memory oTransaction2 = oTransaction
You can release an object the same way you release a memory variable:
When you release an object, its Destroy event is fired, and any code defined in the Destroy() event method is executed. It’s important to note that the object remains in memory until all references to it have been released.
Subclassing user-defined classes
Not only does Visual Foxpro allow you to create new classes based on the built-in base classes, but you can also create new classes based on your own user-defined classes. For example:
DEFINE CLASS BaseForm AS Form
DEFINE CLASS MaintenanceForm AS BaseForm
Here we first define a new form class called BaseForm, and then use that form class as the basis for yet another new form class, MaintenanceForm. If we wish, we could then create a new class based on the MaintenanceForm class, and then create a new class on that, and so on, and so on. I would recommend keeping the class hierarchy depth as “shallow” as possible, but without placing a specific number on just how deep to go. Use your best judgment.
We pause for a look at our dictionary ...
Before we get too deep into a sea of terminology, it would be desirable to pause for a moment to examine some terms.
In past articles, we defined the term “superclass” which refers to any class that is being used as the basis for creating other classes. You may have seen other object-oriented texts refer to a superclass as a base class. In fact, this is acceptable terminology. However, it is important to understand that Visual Foxpro uses the term base class to refer to it’s own built-in class hierarchy, not an arbitrary superclass that you create.
Visual Foxpro uses the term “parent class” to mean exactly the same thing as “superclass”. This is unfortunate because it seems that most other object-oriented products and literature have standardized on the term “superclass”. Additionally, Visual Foxpro uses the term “parent” to refer to an object that contains other objects. It can get confusing when trying to talk about the class of an object’s parent vs an object’s parentclass - they are not the same thing.
I bring up this issue of terminology to help you avoid confusion when discussing object-oriented topics with your associates, or when reading a non-language specific object-oriented book or article. (Or when you call the office to invite me over for dinner - you didn’t think I’d forget, did you?). Just remember that in generic object-oriented terms, “base class”, “superclass”, and “parent class” all mean the same thing. But remember that a “base class” is just a “parent class” that has special meaning in Visual Foxpro, and that “superclass” and “parent class” mean exactly the same thing, but “parent class” is preferred.
Back to your regularly scheduled object model ...
So how do we add custom properties to our classes? Here’s the syntax:
DEFINE CLASS Customer AS Custom *-- Custom property definitions cName = “Ivar Jacobsen” nAge = 40 lHasMethodology = .T. *-- Method definitions follow ENDDEFINEBasically, custom properties are defined before any method code for that class. You could also use this space to initialize built-in properties:
DEFINE CLASS MyForm AS Form Caption = “My Form” AutoCenter = .T. BorderStyle = 2 ENDDEFINENote that if you need to initialize a property to the result of an expression or UDF, you’ll have to do this in the Init() event method for the class:
DEFINE CLASS Table AS Custom cFullName = “” FUNCTION Init() this.cFullName = DBF() ENDFUNC ENDDEFINEWhat's THIS all about?
In the Init() event method of the above example, I used the new “this” keyword to refer to the property of the class. Visual Foxpro has added this keyword, along with the “thisform” and “thisformset” keywords to provide access to properties or methods that are scoped to the class, form, or formset, respectively. The following table illustrates this concept further:
|this||Used in method code to refer to a property or method of the current class.|
|thisform||Used in method code in a form to refer to a property or method of the current form. Can be used from anywhere within that form, including methods of controls on that form.|
|thisformset||Used in method code in a formset to refer to a property or method of the current formset. Can be used anywhere within that formset, including methods of forms contained within that form set, or controls contained on any form in the formset.|
Encapsulation is the ability to bind both data and functions (or procedures) to a class. What if you have a situation where you have defined properties or methods for a class that you do not want to be accessed directly using the object.Property or object.Method() syntax? Like any robust object model, Visual Foxpro allows you to do this through use of the PROTECTED keyword.
DEFINE CLASS Customer AS Custom
PROTECTED FUNCTION ChangeName(tcNewName)
this.cName = tcNewName
If we instantiate an instance of class Customer:
oCustomer = CREATEOBJECT(“Customer”)
and attempt to access either the protected property or method of that class, we will get an error:
oCustomer.cName = “Grady Booch” && Error!
oCustomer.ChangeName(“Grady Booch”) && Error!
Why would you want to protect class members? Let’s assume that certain properties of a class represented an object’s state. If you couldn’t prevent those properties from being accessed directly from outside the class, how could you ever guarantee the state of that object? If you protect the property, you could then create a custom method that would be used to assign values to that property. The method could contain validation code to ensure that the value is set properly. Since the property value is assigned in just one place, the code becomes much easier to debug and maintain.
Another reason for having the ability to protect members of a class is when you have utility methods that serve a particular purpose for a specific class, but are not meant to be called from outside that class. Leaving those methods unprotected could have disastrous results!
Add those objects!
A property of a class is not limited to being just a simple variable. It can also be an object of another class. For example, if you want to add a command button to a form in code, you would use the following syntax:
DEFINE CLASS MyForm AS Form
ADD OBJECT oCommandButton AS CommandButton
Once you create an instance of this form, you could then refer to the oCommandButton just like you would a normal property:
oMyForm = CREATEOBJECT(“MyForm”)
? oMyForm.oCommandButton.Caption && Prints the button’s caption
The form is now considered the parent of the command button. In fact, the form can be accessed by referencing the command button’s Parent property. From the Click() event method of the command button:
WAIT WINDOW this.Parent.Caption && Print the form’s caption
The Visual Foxpro object model is based on a solid foundation of object-oriented concepts that have been in place for years. As a result, developers will be able to create more complex applications that are easier to debug, easier to maintain, and meet user requirements more closely than ever before.