OOP is Not an Accident!
The poetical language of an age should be the current language heightened, to any degree heightened and unlike itself, but not � an obsolete one.
Gerard Manley Hopkins, 1879
We've heard about Object-Oriented Programming (OOP) for more than 20 years. At first, it was just theory. Then, we started hearing about this language called SmallTalk. In the last few years, OOP's been everywhere, and the claims of who has it and how it revolutionizes programming have grown louder and louder.So what is OOP? Why is it important? And has it really changed the way we program?Object-Oriented Programming is a different way of looking at programming than the procedural approach found in traditional programming languages. (It's different than the "give me what I want—I don't care how" approach of SQL, too.) Conventional programming languages look at the world in terms of actions—first you figure out what to do, then you figure out what to act on. OOP looks at the world in terms of objects—first you figure out what objects you want, then you figure out what actions the objects should take.OOP is not a replacement for everything you have learned about making a computer work up to this point. We know naysayers who claim you need to throw out everything you've done before and start from scratch. That's just not true. OOP is a better, more realistic way of looking at the processes and entities and their interactions, modeling them, describing them, abstracting them and enhancing them, but it does not change the requirements of our systems to perform the functions they do. It just looks at packaging them differently. Consider the FoxPro 2.x way of maximizing a window: ZOOM WINDOW MyWindow MAX. You start out by indicating the action you want to take (ZOOM WINDOW), then indicate what object to apply the action to (MyWindow). The OOP equivalent is MyWindow.WindowState = 2. You start out by indicating that you want to deal with MyWindow, then you narrow it down to MyWindow's WindowState. Finally, you indicate what you're doing—setting that state to 2 (which is maximized).This may seem like only a minor syntactic difference, but in fact, we're really turning the whole process on its head. The hidden difference here is the change from passive elements to active elements. In procedural code, windows, controls, and so on are all passive things that we act on with commands. In OOP, windows, controls, and so on are active objects that know how to do things—we simply tell them what we want them to do. (See "The Message is the Medium" below for more on this.) It's worth noting that this difference is also the major one between the DOS and GUI worlds. In DOS, you use a Command-Object syntax: What shall I do? and, by the way, who should I do it to? In graphical environments like Windows and Mac, you choose a thing and then decide what to do to it. It's not a surprise, then, that the big move to object orientation has come with the move to GUIs—it's much easier to program when your language works like your environment.The Object of My Affections
Nothing can have value without being an object of utility.
Karl Marx, Capital
Not surprisingly, the basic unit in OOP is an object. An object is an entity that has some characteristics and knows how to do some things. For example, a form is an object. So is a check box or a grid.The formal name for the object's characteristics is properties. The official name for the things it knows how to do is methods. (There are some special methods called events that react to user and system actions—see "A Gala Event" for more on this subject.)Object orientation is really packaging. We put the properties together with the methods so the object is self-contained. To use the object, you don't need to depend on anything outside the object itself. This essential feature of object orientation is called encapsulation—that's one of several rather intimidating words you'll find floating around the OOP world.Encapsulation is really pretty simple. Instead of putting data in one place and code that operates on it in another (the traditional procedural division), you package them together, so when you have one, you also have the other. If you've ever worked with abstract data types, you're familiar with this concept. If not, we suspect it'll grow on you pretty quickly.Don't confuse the data that's encapsulated in objects with your database data. Although there's talk about object-oriented database management systems, we're not dealing with those here. The properties of an object are its data; the methods are the code. Perhaps information would be a better term here: Encapsulation is the feature where an object possesses the information about itself and the method code to act upon that information.A Class Act
To be an Englishman is to belong to the most exclusive class there is.
Ogden Nash
Where do objects come from? Well, there's the Mama object and the Papa object and they get together with the birds and the bees and nature takes its course. Oops, wrong kind of object.So where do objects come from? We don't just pull them out of thin air. Instead, we base each object on a class. The class forms a blueprint or template for the object, and we can create as many objects as we'd like, based on a single class. (More on where these class things come from below.)The class definition determines what properties and methods an object has. Every object based on a particular class has the same set of properties and methods. What distinguishes one object from another is the values of those properties. (For example, one check box might have its caption to the left while another has the caption to the right. All that's been changed is the value of the Alignment property.)Visual FoxPro provides two ways of defining classes: in code and through the Visual Class Designer. (Actually, there's a third way—using the Form Designer—but that's really a variation on using the Class Designer.) In either case, though, you do the same kinds of things: Specify the properties and methods of the class, and indicate initial values for the properties.The act of creating an object based on a class is called instantiation; the object created is called an instance of the class.That's Quite an Inheritance
I would rather make my name than inherit it.
William Makepeace Thackeray
Suppose you have a class that's almost what you want for some purpose, but not quite. The procedural thing to do is to copy the class and make the changes you need. But then you're stuck with two different classes to maintain. What if you find a bug in the original? You have to remember to change not just the original, but also the copy you modified. And what if you then need another class a little different than either the original or the modified version? You copy and change it again. Now you've got three things to maintain. And it keeps getting worse and worse.One of the most powerful features of object orientation is the ability to create subclasses of existing classes. The subclass inherits the properties and methods of the original, but allows you to make changes. Most important of all, changes to the original class are inherited by the subclass.Inheritance is the second of three characteristics a language must have to be considered object-oriented. (It's also the one missing from Visual Basic versions prior to VB.NET—VB is considered to be object-based, not object-oriented.)So where do all these classes come from anyway? No, not Mama and Papa classes—you've been dozing through our explanation. All classes, ultimately, are descendants of one of the base classes built into the language. In the case of Visual FoxPro, we've been supplied with a rich set of base classes from which all of our classes are built. More on these a little later. Our favorite example of inheritance is pretty simple. Say you work for LargeCo, a large corporation, and there's a corporate standard for input forms. The standard includes the corporate logo as wallpaper. Then, LargeCo is gobbled up by EvenLarger Corporation, which declares that all forms must have its corporate logo as wallpaper.In FoxPro 2.x, you might have handled the original requirement by saving a screen containing just the logo and copying it as the basis for all your new screens. Works great until EvenLarger comes along—then, you have to go back to every screen you've created and change the wallpaper.Okay, how does this work in Visual FoxPro? You start off by subclassing VFP's base form class. In your subclass, you set up the wallpaper with LargeCo's corporate logo. Now, whenever you need a new form, you subclass it from your corporate form and start building. Doesn't seem so different from what you did in FoxPro 2.x.But here's the payoff. Along comes EvenLarger—what do you have to do? You go back to your original subclass (the one based on VFP's form class). You change the wallpaper to the new corporate logo and voila! All your other forms inherit the change. That's right—with inheritance, you make the change once!So are you sold yet? We are.Inheritance is actually even more powerful. Not only does a subclass inherit the properties and methods of the class it's based on, it also inherits the code in the methods. You can override that code by specifying different code for the same method of the subclass. If you don't override, it's as if you'd put the inherited code right in the subclass' method. (See "Hierarchies and Lower-archies" below for how to have the best of both worlds.)Polymorphism is Mighty Morphism
There's one more key feature of object orientation, and its name is even more obscure than encapsulation or inheritance. This one is polymorphism. Actually, though, this one's pretty simple. It means that different objects can have methods with the same name. The methods don't have to behave the same way (though it's a good idea for them to do similar things).In other words, you no longer have to struggle to come up with unique names for minor variations on a theme. Every object can have a Print method—no more PrintInv, PrintCust, PrintThis, PrintThat. Just issue SomeObject.Print and that object gets printed.The Message is the Medium
Okay, we've defined all the buzzwords and talked about objects and classes, but how does all this fit together? The key is in message passing. No, not the like the kind that got you in trouble with your second-grade teacher. Well, maybe like that, actually.The basic idea in OOP is that objects know how to take care of themselves. They contain all their data and a set of actions to perform on that data. But sometimes, they need something from another object to get the job done or they have information that another object needs. So they send a message to the other object, asking that object to do something, asking for information from that object, or telling the other object something important. These correspond roughly to invoking a method of another object, checking a property value from another object, and changing a property value in another object.You can access a property of any object by giving the object's name, then a period ("."), then the property name. For example, the Picture property (which provides the wallpaper) of a form called MyForm is referenced with:MyForm.PictureTo change a property, simply assign it a new value. To give MyForm the Fox bitmap for wallpaper, you'd write something like:
MyForm.Picture = "F:\VFP\Fox.BMP"To store the current value of MyForm's picture property in a memory variable, perhaps so it could be changed and later restored, you'd write something like:
cCurPict = MyForm.PictureYou reference methods similarly. Use the object name, a period, and the method name. If the method accepts parameters, enclose them in parentheses. Parentheses are optional if you're not passing parameters, but we recommend always using them when calling methods. To call MyForm's Refresh method, for example, you can write:
MyForm.Refreshor
MyForm.Refresh()We like the second form better because it makes it clear that Refresh is a method.
The Protection Racket
Woman must not depend upon the protection of man, but must be taught to protect herself.
Susan B. Anthony
Some objects have properties or methods that can be dangerous in the wrong hands. The way you prevent these dangers is by marking those properties and methods as "protected." Protected properties and methods can be accessed only by methods of the same object. If other objects need access to the value of a protected property, you can provide a method (not protected) whose sole function is to return that value.For example, the code in the Developer Downloads for this book (available at www.hentzenwerke.com) contains a class designed to keep track of connections to remote servers. That class uses an array to contain the connection information and has a property indicating how many connections it's currently tracking. Letting the outside world touch that counter property would be suicidal for this class. Instead, that property is protected and there's a Count method that returns the current connection count.
Some OOP theorists believe that all access to properties should come through methods, that no object should be able to directly read or change the properties of another. Visual FoxPro does not follow that philosophy by default, but you can design your classes to do so, if you wish. (See "Assign of the Times" below for another approach to this problem.)Crouching Tiger, Hidden Property
Society is a masked ball, where every one hides his real character, and reveals it by hiding.
Ralph Waldo Emerson, The Conduct of Life, 1860
In VFP 5 and later, you can go even farther to protect properties and methods from outside abuse by marking them as "hidden." Protected properties and methods can be seen in subclasses of the original class; hidden properties and methods cannot. They can be seen only in objects of the class that creates them.The hidden characteristic seems particularly useful when you're building classes to be distributed without source. You can keep some properties and methods from even being visible in subclasses and use them for internal bookkeeping. Unfortunately, VFP keeps this approach from being as useful as it should because hidden methods can't even be accessed indirectly through calls up the class hierarchy. (See "Climbing Trees" below for an explanation of such calls.)Assign of the Times
Remember that you are an actor in a drama, of such a part as it may please the master to assign you, for a long time or for a little as he may choose.
Epictetus, Encheiridion, no. 17.
We said above that some people think one object should never directly change the properties of another. The reason is that the object being changed doesn't know it's being changed. VFP 6 gives your objects the opportunity to know when they're being changed and even when they're being used (a feature we might have found handy at some time in our lives).Each property can have two events associated with it automatically: an Access method and an Assign method. When a property has an Access method (the method name is propertyname_Access), that method is called whenever someone reads the value of the property. An Assign method (named propertyname_Assign) is called whenever someone changes the value of the property. You can put code in these methods to prevent the access or assign, to log it, or to do anything else you want. In addition to Access and Assign methods for properties, you can also create a method for all objects called This_Access. If it exists, this method is fired when any member of the object is accessed. This_Access accepts the member object reference as the parameter, so you know what was accessed. It must return an object reference, too (or you'll see error 9, "Data type mismatch"). It doesn't replace a property's Access method, because you can't control the values that are returned to the property, but you can take action based on what was accessed. This_Access fires before the property's Access method.Hierarchies and Lower-archies
In a hierarchy, every employee tends to rise to his level of incompetence.
Laurence J. Peter, The Peter Principle, 1969
One of the most confusing aspects of object-oriented programming is that there are two different hierarchies at work. We mentioned above that you can create subclasses based on existing classes. In fact, you can do this over and over again, building a tree (the computer science kind of tree, not the nature kind of tree) of classes of as many levels as you'd like. At the top of this tree, known as the class hierarchy, are Visual FoxPro's base classes—more on those a little further along. The next level contains subclasses derived directly from base classes. At the next level are subclasses derived from the subclasses one level up. And so on and so forth.The reasonably standard OOP term for the class one level up the hierarchy is superclass. For reasons we can't comprehend, Visual FoxPro instead calls the class one level up the hierarchy the parentclass. Reminds us of the old joke, "How many Microsofties does it take to change a light bulb?" — "None, they just declare darkness the new standard."Inheritance applies to the class hierarchy. An object based on a class at the bottom of the tree has properties and methods specified for that class, plus it inherits any properties and methods of its parentclass, and those of the parentclass of its parentclass (you might call it the grandparentclass) and so on, all the way back to the root of the tree.When a method of an object is called, the class the object is based on is checked. If it has code for that method, the code is executed. If there's no code there, we start climbing the class hierarchy, looking for an ancestor of this class that has code for the specified method. As soon as we find some code for that method, we execute it and stop. But until we find it, we keep climbing the tree until we reach the Visual FoxPro base class the object is ultimately derived from. Even if no code is specified anywhere on the tree, if the base class has some default behavior, like redrawing the object upon invocation of the Refresh method, that behavior occurs. (Actually, that base behavior normally occurs even if there's code somewhere in the hierarchy—see "Ain't Nobody's Default But My Own?" later in this chapter.)Contain Yourself
Now what about the other hierarchy we mentioned? This comes from the fact that one object can contain another. For example, a form is an object (based on the Visual FoxPro Form base class). A form usually contains all kinds of other objects like text boxes, check boxes, grids, and so forth. Some of the objects in a form can contain other objects. For example, a grid contains columns, which in turn can contain headers and controls.So the second hierarchy is the containership hierarchy. This is the map of what's inside of what. The most important point about the containership hierarchy is that inheritance has nothing to do with it at all. Objects do not inherit anything from their containers or from the objects they contain.The second most important thing about the containership hierarchy is that the term "parent" is used here, too. (This is one reason we're frustrated by Microsoft's choice of parentclass over superclass.) The parent of an object is the object that contains it. For example, the parent of a column is the grid containing that column. The parent of a text box might be a form. Don't confuse "parent" with "parentclass"—they really are two different things.One other terminology note: The objects inside another object are called members of the containing object. The term "members" is also used more broadly to refer to the properties and methods of an object, as well as to the objects it contains.Climbing Trees
At various times, we need to climb each of the hierarchies. Let's start with the class hierarchy. Say you're defining a subclass and, for some method, you want to do everything the parentclass does, but then do a few more things. Your first instinct might be to copy all the code from that method of the parentclass into the corresponding method of the new subclass. Why shouldn't you do this?What happens if you have to change the parentclass' behavior? If you've done cut-and-paste to the subclass, you're out of luck. The changes aren't inherited.So what should you do? Call the parentclass' method explicitly from the subclass' method. There are two ways to do this. One way uses a notational trick, since you can normally only call methods of objects, not of classes. A special operator "::" lets you call up the class hierarchy—the notation is:ClassName::MethodThe second way to call up the class hierarchy is by using the DoDefault() function. This function, which can only be used in method code, calls the same method one level up the hierarchy—it was added in VFP 5.Both DoDefault() and the "::" operator let you have your cake and eat it, too. A subclass' method can call the same method of its parentclass and then do some more work. Or, if you prefer, it can do the extra stuff first and then call the parentclass' method. Or both: Do something, call the parentclass' method, and then do something else. A common term describing this is augmenting the parent's method. If you completely change the method, and don't call the parent method (a clue there's a design problem), this would be termed overriding the parent method.Moving around the container hierarchy is actually a lot more common and uses the dot notation. To send a message from one object to another, you have to be able to find the recipient. You can do that by walking down the container hierarchy until you reach the object you want. For example, to refer to a spinner named spnDays on a page called pagPage1 of a page frame called pgfCalendar of a form called frmWhoKnows, you write:
frmWhoKnows.pgfCalendar.pagPage1.spnDaysWhat a mouthful!You want class definitions to be as reusable as possible. Because you might create many instances of a single class, you don't know when you're writing code what the name of the actual object will be. You also may not know what the parent of an object is. For example, a text box might be contained by a form, a column or a page (of a page frame).A special operand, This, lets you refer to the current object without knowing its name. The Parent operator lets you move one level up the container hierarchy without knowing what's up there. For example, to find the name of the parent of the current object, you'd write:
This.Parent.NameYou can use Parent repeatedly to climb multiple levels:
This.Parent.Parent.Parent.Leftgives you the left edge of the object three levels up in the hierarchy. If This is a check box in a column of a grid on a page of a page frame, that expression would refer to the page's Left property.Because you don't always know how deep in the hierarchy you'll be, This has two cousins, ThisForm and ThisFormSet, which let you jump quickly to the top of the container hierarchy. Then you can climb back down one level at a time. Say you want to address the button cmdSave that's on the current form. You can reference it with:
ThisForm.cmdSavewithout worrying about where you are now on the form.
Base Clef
All fantasy should have a solid base in reality.
Sir Max Beerbohm, Zuleika Dobson, 1911
Visual FoxPro comes with a set of built-in classes known as the base classes. FoxPro's base classes cannot be changed, but most of them can be subclassed. In fact, we recommend that one of the first things you do is subclass all the input controls and build your own forms and form classes from your subclassed controls, rather than from FoxPro's base class controls. We suggest you do this even if you change not one thing about the control because someday, you're going to want to make changes. (Starting with VFP 6, Microsoft has provided a set of "one-off" classes to start from, as part of the FoxPro Foundation Classes (check out the HOME()+"FFC\" directory) so you don't have to do this yourself anymore.) If you've used the base classes in your forms, there'll be a lot of work ahead.There are several ways to break the Visual FoxPro base classes into groups. The biggest division seems to be between containers and non-containers. Containers can hold other objects while non-containers can't—simple enough. There's also the question of whether a class can be subclassed in the Class Designer. Then, some classes are visible while others aren't. Finally, different classes came into the language at different times. The table below shows all of Visual FoxPro's base classes and classifies them according to all four criteria.
Base Class |
Container? |
Subclass-able |
Visible? |
Version |
ActiveDoc |
No |
Yes |
No |
VFP 6 |
Checkbox |
No |
Yes |
Yes |
VFP 3 |
Column |
Yes |
No |
Yes |
VFP 3 |
ComboBox |
No |
Yes |
Yes |
VFP 3 |
CommandButton |
No |
Yes |
Yes |
VFP 3 |
CommandGroup |
Yes |
Yes |
Yes |
VFP 3 |
Container |
Yes |
Yes |
Yes |
VFP 3 |
Control |
Yes |
Yes |
Yes |
VFP 3 |
Cursor |
No |
No |
No |
VFP 3 |
Custom |
Yes |
Yes |
No |
VFP 3 |
DataEnvironment |
Yes |
No |
No |
VFP 3 |
Editbox |
No |
Yes |
Yes |
VFP 3 |
Form |
Yes |
Yes |
Yes |
VFP 3 |
FormSet |
Yes |
Yes |
Yes |
VFP 3 |
Grid |
Yes |
Yes |
Yes |
VFP 3 |
Header |
No |
No |
Yes |
VFP 3 |
Hyperlink |
No |
Yes |
No |
VFP 6 |
Image |
No |
Yes |
Yes |
VFP 3 |
Label |
No |
Yes |
Yes |
VFP 3 |
Line |
No |
Yes |
Yes |
VFP 3 |
ListBox |
No |
Yes |
Yes |
VFP 3 |
OLEBoundControl |
No |
Yes |
Yes |
VFP 3 |
OLEControl |
No |
Yes |
Yes |
VFP 3 |
OptionButton |
No |
No in VFP 3 |
Yes |
VFP 3 |
OptionGroup |
Yes |
Yes |
Yes |
VFP 3 |
Page |
Yes |
No |
Yes |
VFP 3 |
Pageframe |
Yes |
Yes |
Yes |
VFP 3 |
ProjectHook |
No |
Yes |
No |
VFP 6 |
Relation |
No |
No |
No |
VFP 3 |
Separator |
No |
No in VFP 3 |
Yes |
VFP 3 |
Session |
No |
No |
No |
VFP 6 SP 3 |
Shape |
No |
Yes |
Yes |
VFP 3 |
Spinner |
No |
Yes |
Yes |
VFP 3 |
Textbox |
No |
Yes |
Yes |
VFP 3 |
Timer |
No |
Yes |
No |
VFP 3 |
ToolBar |
Yes |
Yes |
Yes |
VFP 3 |
Not Quite All There
Since in true OOP, every class can be subclassed, we like to think of those base classes that can't be subclassed in the Class Designer as being "half-classed." For the most part, each of these classes is a necessary component of some container class. You can subclass these classes in code, but you still can't incorporate your subclasses in the containers (or subclasses of them) that normally contain the base classes. For example, you can subclass Grid, but your subclass will still be made up of Columns, which will still contain Headers. Similarly, Pageframes always contain Pages; you can't base a Pageframe subclass on a subclass of Page.Even with CommandButtons and OptionButtons (which you can subclass visually), when you make a CommandGroup or an OptionGroup, it's always built of CommandButtons or OptionButtons—you can't build it out of a subclass.We can see the reason for this limitation, but we keep hoping it will go away.Other classes have similar limitations in that they can only be created in code (Session). These are half-classed in their own way, too, in that they don't allow Visual FoxPro programmers the range of tools available within the development environment, or respect the individual programmer's preference for visual or code-based tools.The details of each of the base classes are discussed in the Reference section, so we won't go into them here.Ain't Nobody's Default But My Own?
Certain behaviors are built into Visual FoxPro's base classes. For example, when you press a key, that keystroke is placed in the keyboard buffer. When there are tables or views in the data environment, you don't need to write code to open them and set up the specified relations. Generally, these behaviors are tied to certain events. The keystroke entering the keyboard buffer is part of the KeyPress event. Opening tables and setting relations is part of the OpenTables method (which behaves more like an event in many ways).Even if you override the method code for these events with your own code, these default behaviors occur anyway. And that's a good thing. You wouldn't want to have to code your own version of putting a keystroke in the keyboard buffer or opening the tables in the DE. Nor would you want to have to call up to the base class every time you override a method.But, once in a while, you want to override the default behavior as well. Perhaps you want to eat the keystroke because you're doing something special with it. Sure enough, there's a way to handle it. To prevent an event from firing its default base class behavior, put the command NoDefault on a line anywhere in the method for that event. Since the base class default behavior always happens last, NoDefault can go anywhere in the method code.NoDefault and DoDefault() are two more places where the words used for things cause confusion. Given their names, it's not unreasonable to think that they're exact opposites. It's not unreasonable; unfortunately, it's also not true. NoDefault turns off the built-in behavior of a method; it has no effect on user code. DoDefault() executes the user code for a method one level up the class hierarchy and can cause the built-in behavior to occur earlier than it otherwise would. It's not at all uncommon to have both NoDefault and DoDefault() in the same method code. The most common reason to combine them is to make the built-in behavior happen sooner. For example, we sometimes issue NoDefault followed by DoDefault() at the beginning of the OpenTables method. Then, we can add some code to do things like create indexes for views that were opened.Taking Some Extension Classes
At the same time as VFP has been OOP-ified, so have a lot of other parts of the programming world. In particular, many of the tools for allowing applications to interact now use object-oriented techniques. This includes ActiveX, COM Automation and various data access technologies, like RDO and ADO.What this means to you is that you'll find yourself writing OOP-y code not just to handle tasks in VFP, but to handle much of the interaction with other applications. For example, Automation with Word or Excel involves creating an Automation object and then setting properties and calling methods. So does using ADO to access non-VFP data. In addition, a number of objects that are accessible directly from VFP (that is, without having to explicitly create them) are really ActiveX objects. For example, the Project and File objects added in VFP 6 use ActiveX technology. You can't subclass them in VFP, but you talk to them through properties and methods.Give Me the Whole ScOOP
We've just skimmed the surface on object orientation here. After working with it for several years now, the OOP way of looking at things feels pretty natural to us. So many tasks are performed more simply using OOP.But using OOP effectively is more than just a code thing. Designing applications to take advantage of OOP requires a new way of looking at them. Each version of VFP since the introduction of OOP has more tools—like the Class Browser, IntelliSense, and the Object Browser—to help implement and use good object-oriented principles, but the tools don't take the place of a good knowledge of how OOP works.Design Isn't Just for Art Majors
Design is perhaps more important in the OOP world than it is in the structured programming world. (OK, we can argue that design is terribly important regardless of what kind of programming you're doing—but if you're arguing, you probably already know the importance of design. If you're not arguing, you need to know that design is important!) There's so much to say about design issues. How do you know if your object design is right or wrong? Why do you design to interface, not implementation? Why do you design for the general case and code for the exceptions? There's a Hentzenwerke book dedicated to OOP, Advanced Object Oriented Programming with VFP by Markus Egger (see www.hentzenwerke.com for more information). Also check the appendices for some references on object-oriented analysis and design.