Visually Impaired: Visual FoxPro for 2.6ers - Non-Visual ObjectsRobert C. Bradley
Kicking and screaming
Like most developers who started in the xBase world and subsequently migrated to Foxbase and FoxPro, I bit the bullet several years ago and begrudgingly learned to develop using object-oriented programming. I did so because I knew I had to in order to get the most from Visual FoxPro, and because...well, everyone else was.
I recently worked on a project that made clear the advantages of object-oriented programming (OOP). But it made the point like a Ken Burns documentary: it showed me the way things were, seemingly a hundred years ago. Because the project was strictly procedural (data import with many rules and checks applied); there was no user-interface component; and the client was more comfortable with procedural design and programming, I coded the system in Visual FoxPro but using a procedural, rather than object-oriented, approach.
It was like being transported back to the mid-1800's. I realize that the past has its charm, but we often forget that disease was rampant, life was hard, workdays were 14 hours long and there was no pizza delivery. Likewise, the techniques that I had come to depend on in OOP could not be used in 2.6-style procedural development, and I found out through my time-travel just how dependant I had become on those techniques.
What's that, you say? Processes like data imports and data massaging should be done in procedural style? It is a natural reaction; but OOP is not defined by forms, command buttons, and data environments. Its power is more universal.
Not just a pretty face
After using Visual FoxPro for a while, it is natural to think of OOP in terms of the objects we use most often: forms, command buttons, text boxes, and so forth. Since I said earlier that this project had no user-interface piece, it would seem obvious (if not much simpler and easier to code and maintain) that straight procedural methodology was the way to go.
But OOP gives us some help in ways that we (or at least I) take for granted. Not just a different way of calling subroutines, but a regimen of organization that is often missing (or not easily possible) in procedural programming.
Linguini or Vermicelli, its still pasta
A sinple puzzle: assume that we are importing clients. We might think of a client as a single logical record, but in fact they may be eventually stored as separate name, address, and telephone records (depending on how normalized we get). Let's pretend we have a subroutine called ChkAreaCode that checks for an area code in the incoming telephone data; if one is not included, a default value is supplied.
Here's the million-dollar question: where can I find the code for the subroutine ChkAreaCode ?
The answer, of course, cannot be determined. It could be in a separate PRG file; it could be contained in another PRG and is found due to a SET PROCEDURE TO that is in effect; it could be contained in the current PRG; or, for all I know, it could be a built-in VFP function (alright, obviously not in this case, but you get the idea).
While not as bad as the old GWBasic GOTO command that made tracing a program's flow like finding both ends of a piece of spaghetti, finding where code resides is still a mess.
NVO to the rescue
Enter non-visual objects. In the above puzzle, if I knew the system was written OOPishly, then I would make a rather educated guess that the ChkAreaCode method was probably in the Telephone object.
Hah! You say; "probably" dilutes your point. Not at all, because when the
ChkAreaCode method was called, its container is shown quite clearly, as in
oImportData.oTelephone.ChkAreaCode()From this example, we see that the ChkAreaCode method is contained in the Telephone object, which itself is contained in the ImportData object.
Object-oriented programming forces us to think in terms of entities (Telephone, for example). While it may seem awkward at times, it also forces us to organize our code much better than we might if using procedural techniques. As another example, if I had a generic "open a table" method, I would be fairly certain it would be contained in my oImportData object (which is probably a subclass of my generic System object). This technique produces a system that is much easier to read and maintain than one with scattered PRGs and procedure files.
Creating non-visual objects
The following example shows the code that creates a Telephone object, and defines several properties and methods for it:
define class telephone as custom cAreaCode = "" && creates cAreaCode property cNumber = "" && creates cNumber property cDefaultAC = "512" && creates cDefaultAC property procedure ChkAreaCode if empty(this.cAreaCode) or isnull(this.cAreaCode) this.cAreaCode = this.cDefaultAC endif procedure SaveRec insert into telephone ; (AreaCode, Number) ; values ; (this.cAreaCode, this.cNumber)
Now we have created the class and given it some properties and some methods. To illustrate the difference between the procedural and object-oriented approaches, let's look at how these would differ in each methodology:
local lcAreaCode, lcNumber lcAreaCode = mycursor.AreaCode lcNumber = mycursor.Number do ChkAreaCode with lcAreaCode, lcNumber do SaveRec with lcAreaCode, lcNumber
oTelephone = createobject("telephone") oTelephone.cAreaCode = datain.AreaCode oTelephone.cNumber = datain.Number oTelephone.ChkAreaCode oTelephone.SaveRec
"Hey, wait a cotton-pickin' minute!" you say; you intentionally wrote these so that the procedural way would take as many lines of code! True, I did not need to assign the data to memory variables in my simple example. But the fact is that VFP's object-oriented methodology freed me from having to worry about such things, whereas variable scoping is (or should be) a constant concern in procedural programming. I no longer need public memory variables or worry as much about what variables are in scope, hidden, or about the be trounced upon.
The point should be clear: the OOP version is easier to follow, and probably easier to maintain, because there is less ambiguity about where the code resides. This is especially important in our growing outsourcing of programming environment, where the person(s) who created the code is no longer around and it is the client who must maintain it.