foxtalk - dfpug-portalportal.dfpug.de/dfpug/dokumente/foxtalk/pdf2000/ft0100.pdfjanuary 2000 volume...

35
January 2000 Volume 12, Number 1 Fox Talk Continues on page 5 Solutions for Microsoft® FoxPro® and Visual FoxPro® Developers Accompanying files available online at http://www.pinpub.com/foxtalk Applies specifically to one of these platforms. Applies to FoxPro v2.x Applies to VFP v5.0 Applies to VFP v3.0 Applies to VFP v6.0 (Tahoe) 6.0 6.0 Using VFP to Visualize 3D Molecular Models Robert Stone Ever wonder what those form-level graphics methods could possibly be used for? If you can think of line() and circle() in terms of sticks and balls, you can imagine drawing atoms, chemical bonds, and molecules. Add a little mathematics, and Robert Stone shows you how you can draw and rotate molecular models in three dimensions. I F you had the good fortune of taking chemistry in school, you probably spent some time building stick-and-ball models of molecules. These structures are valuable to chemists, for they convey information about chemical classes and properties, reactivity, and other data. Visual FoxPro forms contain the methods line() and circle(), which can draw “sticks and balls” directly on the form’s surface. Since data files containing the 3D structure of molecules are widely available, FoxPro can be used to display these structures, and with a little code, the user can rotate the molecule in three dimensions. Figure 1 (on page 5) is an image of the form I built that accomplishes this. The left side of the form is dedicated to the image of the molecule, and the right side to the controls needed to manipulate the image. The image area, or “viewport,” is a shape that’s 200 pixels square with an opaque white fill. The molecule is drawn at its center—that is, at form coordinates 100, 100. Listing 1 (on page 5) shows the layout for the file that holds the data for a simple cube (CUBE.MOL) with alternate corner atoms colored red or blue. This is not a FoxPro table, but a simple ASCII text file that’s read using FoxPro’s low-level file functions. This file format is similar to many molecular structure files you’ll find on the Internet. 6.0 6.0 1 Using VFP to Visualize 3D Molecular Models Robert Stone 3 Editorial: VFP Data on Your Palm Pilot Whil Hentzen 9 Breath Mint? Candy? No, It’s ASSERT Jim Booth 11 Best Practices: Seeing Patterns: The Factory Method Jefferey A. Donnici 14 Persistence Without Perspiration Doug Hennig 19 The Kit Box: Easy as Falling off a Log Paul Maskens and Andy Kramek 23 January Subscriber Downloads EA Visual FoxExpress 6.0 Hector Correa EA Deployment: Walkthroughs Richard A. Schummer

Upload: others

Post on 19-Sep-2019

3 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

January 2000Volume 12, Number 1

FoxTalk

Continues on page 5

Solutions for Microsoft® FoxPro® and Visual FoxPro® Developers

Accompanying files available onlineat http://www.pinpub.com/foxtalk

Applies specifically to one of these platforms.

Applies toFoxPro v2.x

Applies toVFP v5.0

Applies toVFP v3.0

Applies to VFPv6.0 (Tahoe)

6.06.0

Using VFP toVisualize 3DMolecular ModelsRobert Stone

Ever wonder what those form-level graphics methods could possibly be used for? Ifyou can think of line() and circle() in terms of sticks and balls, you can imaginedrawing atoms, chemical bonds, and molecules. Add a little mathematics, andRobert Stone shows you how you can draw and rotate molecular models inthree dimensions.

IF you had the good fortune of taking chemistry in school, you probablyspent some time building stick-and-ball models of molecules. Thesestructures are valuable to chemists, for they convey information about

chemical classes and properties, reactivity, and other data. Visual FoxPro formscontain the methods line() and circle(), which can draw “sticks and balls”directly on the form’s surface. Since data files containing the 3D structure ofmolecules are widely available, FoxPro can be used to display these structures,and with a little code, the user can rotate the molecule in three dimensions.

Figure 1 (on page 5) is an image of the form I built that accomplishes this.The left side of the form is dedicated to the image of the molecule, and theright side to the controls needed to manipulate the image. The image area, or“viewport,” is a shape that’s 200 pixels square with an opaque white fill. Themolecule is drawn at its center—that is, at form coordinates 100, 100.

Listing 1 (on page 5) shows the layout for the file that holds the data for asimple cube (CUBE.MOL) with alternate corner atoms colored red or blue.This is not a FoxPro table, but a simple ASCII text file that’s read usingFoxPro’s low-level file functions. This file format is similar to many molecularstructure files you’ll find on the Internet.

6.06.0

1 Using VFP to Visualize3D Molecular ModelsRobert Stone

3 Editorial: VFP Dataon Your Palm PilotWhil Hentzen

9 Breath Mint? Candy?No, It’s ASSERTJim Booth

11 Best Practices: SeeingPatterns: The Factory MethodJefferey A. Donnici

14 Persistence WithoutPerspirationDoug Hennig

19 The Kit Box: Easyas Falling off a LogPaul Maskens and Andy Kramek

23 JanuarySubscriber Downloads

EA Visual FoxExpress 6.0Hector Correa

EA Deployment: WalkthroughsRichard A. Schummer

Page 2: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

2 http://www.pinpub.comFoxTalk January 2000

Page 3: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 3FoxTalk January 2000

From the Editor FoxTalk

VFP Data on Your Palm PilotWhil Hentzen

Yeah, right.Combine that with the facts that WinCE just wasn’t

that stable (the Nino would lock up on me about onceevery two weeks), the sync mechanism was a bear toinstall and keep running, and WinCE machines chew upbatteries faster than VB developers press Ctrl-C, and,well, the Nino is now resting peacefully alongside myKodak DC-50 digital camera and my AT&T wirelessheadset in the box of “PC Toys That Didn’t Work TooWell.” I’m glad I decided not to put more time toward the Nino—as I was reviewing this month’s issue, Philipsjust announced they are discontinuing the Nino and allinvestments in the WinCE platform.

My Palm Pilot V, on the other hand, is a wonderfultool: plenty of memory, half the size of a deck of playingcards, the V needs to be recharged about once a month,and the sync mechanism with Outlook and Day-Timer hasworked flawlessly for half a year now.

Databases for the PalmI’ve been investigating various tools for creating PalmPDA databases that purport the capability of syncing withfiles on a PC. This month, I’ll mention three and provide abrief overview. In the following months, I’ll describe, stepby step, how to move data from a Fox application to yourPalm PDA and back again.

First, I should explain that these tools use the term“database” in a rather loose fashion. They are flat filemanagers—allowing you to define a few columns in asingle table, and perhaps a couple of indexes. In each case,they use the term “database” when you and I would usethe term “table,” but for the sake of consistency, I’ll repeattheir error for the rest of this article. The sophisticatedones allow you to define a type of “lookup,” either withhard-coded values or a reference to a separate lookup file.But E. F. Codd and C. J. Date aren’t going to be awed byany display of relational technology in these tools yet. Sodon’t get your hopes up yet, okay?

All three of these tools reside on your Palm PDA andallow you to define and use one or more databases onyour portable unit in a proprietary file format. Two ofthese tools also have a related application that you run onyour PC (Windows, Mac, Linux, and yes, even DOS) thatconverts the proprietary file after you’ve synced it fromyour PDA to your PC into a CSV (comma-separatedvalues) formatted file.

I’ve included demo versions of each of these

Just about every one of us has a PDA lately. And the mostpopular machine is definitely the Palm Pilot. But unless you’rea closet C guru, you’re stuck with the programs that comewith your PDA, or those that you can download and install onit. But frankly, that can be a lot of work. Having grown tired ofVisual Basic for a while, I’m starting a new column, that will beprovided as an Extended Article with our SubscriberDownloads at www.pinpub.com/foxtalk, that discusses avariety of different ways that you can move data back andforth between your Palm Pilot and your PC—specifically, yourVFP apps.

I’M a pretty impatient individual. One of my favoritelines comes from one of the Addams Family movies,where Wednesday asks for the salt. Morticia

reprimands her, asking, “What’s the magic word?” AndWednesday, in a dead monotone, replies, “NOW!!!!”

Similarly, I want access to my data all the time. Atleast the data that I use frequently. And that would be mycalendar, my address book, and my To Do list.Unfortunately, the apps that come with PDAs that aresupposed to synchronize with your PC are usuallydumbed-down versions of the PC tool. For example, the“To Do” list manager for the Palm Pilot that syncs withOutlook’s To Do list manager has about a third of thefunctionality. Considering that Outlook’s To Do listmanager is fairly lame to begin with, the Palm version isuseless for anyone who has more than three things to doon a specific day.

Thus began the quest to “roll my own.”

Why a Palm Pilot?Before I get started, I suppose I should explain why I’musing a Palm Pilot instead of a WinCE machine. Well,originally, I had a WinCE machine. It was a Phillips Nino,and at first glance, it seemed to be quite the killer PDA.But one of the reasons I bought it was because WinCEsupported VB, and I figured that I could write a little VBapp for my Nino that would transfer data back and forthwith my desktop.

Well, it turns out that there are different versions ofWinCE, an important fact that’s pointed out in the smallprint on page 47 of the manual. The hand-held version ofWinCE that runs on PDAs that have a full keyboard likethe Jornada uses a subset of VB. But the palm-sizedversion of WinCE that runs on machines like the PhillipsNino and the Casseipia must be programmed using C.

Page 4: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

4 http://www.pinpub.comFoxTalk January 2000

programs in this month’s Subscriber Download file incase you can’t wait until next month to start playingwith them.

SimpleDBIn some advertising agencies, it’s common practice todevelop a “throw-away” campaign that’s the first to bepresented to a picky client. The theory is that the clientwill automatically not like the first presentation, and sothe agency presents work that’s purposefully less thantheir best.

The same theory is at work here. Unless yourdatabase needs are really simple, you’d be well-advised toskip by this one right away. I just presented it so thatyou’d appreciate the others more. On the other hand, ifyou’re looking for a simple database application for a“friend” of yours—a less-than-computer-literate spouse,parent, child, or business associate—SimpleDB might bejust right.

This program allows the user to create databases witha customizable heading and up to five fields per database.You can also add free-form notes to each record. It doesn’t(yet) allow sorting or searching or cut and paste betweenitems, and it doesn’t have a facility for transferring databetween your PDA and your PC.

SimpleDB is also currently in beta, but it has been forquite a while, so it’s reasonably stable. The READMEfile—the only documentation that comes with theprogram—has a list of known limitations and bugs.

ThinkDBThinkDB has considerably more functionality thanSimpleDB, and it even boasts a couple of features that mythird selection, JFilePro, does not.

It allows you to create up to 100 simultaneousdatabases (remember, that means “tables” to you and me)on a device (your PDA), and a database can contain up to65,000 records. Each database can have up to 36 user-defined fields, and you can identify a database asbelonging to a category. This last feature allows you toview a list of only select databases—such as business,personal, athletic training, or however else you mightwant to group them.

ThinkDB has a large number of data types, includingtext, integer, long, float, list (a lookup field), date, time,check box, expression (similar to a calculated field in aVisual FoxPro report), primary key, and external join(similar to a foreign key in that it points to a primary keyin another ThinkDB database).

ThinkDB has a mini-Form Designer where you defineyour record entry form. Since it’s very possible that youwon’t be able to display all 36 fields in a ThinkDBdatabase on one “screen” on your PDA, you can define“tabs” in the Form Designer and display each field on aspecific tab. You can create a dozen different “lists”

(ThinkDB’s name for what we think of as a VisualFoxPro local view). These lists each have their own setof columns, sort orders, and default filters.

You also have the ability to enter multi-line data,find on either one or multiple fields, and work with a“home page” that lists all of the ThinkDB databases onyour PDA. The ThinkDB preferences page allows you tochoose eight different settings, including how the mainscreen displays data, whether to confirm before delete,whether to keep the current database sorted while inuse, and whether to open the last database used the nexttime ThinkDB is started.

ThinkDB comes with a Desktop Companion thatopens existing databases for viewing and editing,imports and exports with Access, and does a number offunctions along the lines of VFP’s MODIFYSTRUCTURE facility.

ThinkDB comes with a good-sized online Help filethat has clear descriptions of how to operate theprogram. The only thing I didn’t really like is thewording regarding their registration key setup: “Anincorrect Palm username will create an invalidregistration key. A new registration key may beissued only if the correct name is similar to the oneprovided previously, and the process can take severalweeks/months.”

JFileProProbably the granddaddy of Palm database tools,JFilePro has a predecessor, JFile, that’s still inwidespread use. JFilePro was released late in 1999,and its files aren’t directly compatible with its ancestor(they can be converted with a utility that’s providedwith JFilePro).

JFilePro allows up to 60 databases on a single PDA,20-character field names, 50 fields per database, and4,000 characters per field. Naturally, these aremaximums—the capacity of your Palm PDA mightimpose stricter limits.

You get four functions, or what they call “views.”These include the Main view (a list of all databasescurrently installed), the New/Modify Structure view,the Database view (essentially a Browse), and theRecord view (which allows the display and editingof a single record).

JFilePro provides most of the same capabilities asThinkDB, including multiple field types, a robustPreferences selection, and standard add/edit/deletefunctionality. It doesn’t have the same type of “lookup”feature, nor does it allow you to relate multipledatabases using keys like ThinkDB does. However, whatit lacks in “relationality” it more than makes up for insorting, searching, and filtering.

You can view column totals, control columnContinues on page 23

Page 5: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 5FoxTalk January 2000

Listing 1. Content of the file CUBE.MOL—this file represents acube with alternate atoms colored red and blue.

* Format of the Data File* Line 1: Molecule Name* Line 2: # of Atoms (nAtoms)* Atom Coordinates: n, x, y, z, radius, color* Line 3+nAtoms: # of bonds* Bond Lines: start_atom_# , end_atom_#

Simple Cube 8 1, 0, 0, 0, 7, 255 2, 1, 0, 0, 7, 16711680 3, 1, 0, 1, 7, 255 4, 0, 0, 1, 7, 16711680 5, 0, 1, 0, 7, 16711680 6, 1, 1, 0, 7, 255 7, 1, 1, 1, 7, 16711680 8, 0, 1, 1, 7, 25512 1, 2 1, 5 1, 4 2, 3 2, 6 3, 4 3, 7 4, 8 5, 6 5, 8 6, 7 7, 8

There are basically two sections to the data, each withsome header information. Line 1 is the name of themolecule; this is displayed on the form’s caption. The first

control is set to 7-Files, and the RowSource to *.MOL.

Attacking the dataThe steps necessary to draw the molecule are: 1) read thedata file; 2) scale the data to fit the viewport; and 3) drawthe molecule. The rotations are accomplished bycalculating the new positions of the atoms based on thedirection and degrees of rotation selected by the user. I’vecreated custom form methods for each step.

Step 1: Reading the data fileThe custom form method ReadDataFile() handles thistask. Low-level file functions are used to read the fileline-by-line. Using CUBE.MOL as an example, themolecule’s name is appended to the form’s caption. Thenumber of atoms on line 2 is stored to a custom formproperty, jnAtoms.

The lines representing the individual atoms are readas strings into a local one-dimensional array, jaAtoms[],using the value of line 2 as the index for the array and as aloop index to read the appropriate number of lines.Similarly, line 11 in the cube file tells us how many bondpairs there are; these are read into a local array, jaBonds[].The file is then closed.

The method loops through these arrays to parse thestrings into individual numeric values in two two-dimensional form array properties—ThisForm.AtomArray[] and ThisForm.BondArray[]. Notice that the datais never stored in a FoxPro table. As you’ll see later, theatom coordinates are sorted frequently, and FoxPro’s

Figure 1. This form allows you to rotate the image of a molecule in three dimensions. Inthis case, the molecule is heme, the oxygen-binding component of hemoglobin. Thelarge, central atom is iron, the four adjacent atoms are nitrogen, the small dark atoms arecarbon, and the large, lighter atoms are oxygen. Following common conventions,hydrogen atoms have been omitted.

3D Molecular Models...Continued from page 1

section holds the coordinates for theatoms, and the second section holdsinformation about how the atoms arebonded together. Line 2 is thenumber of atoms in the molecule.Lines 3 through 10 are comprised ofsix comma-separated numeric values:the atom number (assignedsequentially), the X, Y, and Zcoordinates, an atomic radius, and acolor from FoxPro’s RGB() function.

Section 2 starts at line 11 with thenumber of bonds in the molecule(12), and the remaining lines are the12 numeric pairs corresponding tobonds between the atoms in section1. For example, atom 1 has a bond toatoms 2, 4, and 5.

There are several MOL filesavailable in the SubscriberDownloads file at www.pinpub.com/foxtalk; these are selected from thedropdown list box at the top of theform. The RowSourceType of this

Page 6: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

6 http://www.pinpub.comFoxTalk January 2000

native array sorting function, ASORT(), will work muchfaster than trying to sort or reindex a table.

* Format of the Data File* Line 1: Molecule Name* Line 2: # of Atoms (nAtoms)* Atom Coordinates: n, x, y, z, radius, color* Line 3+nAtoms: # of bonds* Bond Lines: start_atom_# , end_atom_#

LPARAMETERS jcFilenameLOCAL jnFileHandle, jaAtoms[1], ; jaBonds[1], jnCommas[1]

jnFileHandle=FOPEN(jcFileName)

IF jnFileHandle<0 ThisForm.lblAtoms.caption="Atoms: 0" ThisForm.lblBonds.caption="Bonds: 0" wait window "Unable to open file." returnENDIF

* Molecule name on Line 1ThisForm.caption="Spin: "+fgets(jnFileHandle)

* Read # AtomsjnAtoms=VAL(FGETS(jnFileHandle))ThisForm.jnAtoms=jnAtomsThisForm.lblAtoms.caption="Atoms: "+ ; alltrim(str(jnAtoms))

* Read atom data as an array of strings

DIMENSION jaAtoms[jnAtoms]FOR i=1 to jnAtoms jaAtoms[i]=FGETS(jnFileHandle)ENDFOR

* Read # BondsjnBonds=VAL(FGETS(jnFileHandle))ThisForm.jnBonds=jnBondsThisForm.lblBonds.caption="Bonds: "+ ; alltrim(str(jnBonds))

* Read bond data as an array of strings

DIMENSION jaBonds[jnBonds]FOR i=1 to jnBonds jaBonds[i]=FGETS(jnFileHandle)ENDFOR

* Close the data fileFCLOSE(jnFileHandle)

* Parse strings into form array values.* This code checks all of the commas in the first* array element, then assumes all are the same* thereafter. This is done for speed!

* Atoms: format is #, x, y, z, radius, color

DIMENSION jnCommas[5]FOR i=1 to 5 jnCommas[i]=AT(',',jaAtoms[1],i)ENDFOR

* Populate atom array: ThisForm.AtomArray[]

DIMENSION ThisForm.AtomArray[jnAtoms,6]

FOR i=1 to jnAtoms ThisForm.AtomArray[i,1]= ; VAL(SUBSTR(jaAtoms[i],1,jncommas[1]-1)) ThisForm.AtomArray[i,2]= ; VAL(SUBSTR(jaAtoms[i],jnCommas[1]+1, ; jnCommas[2]-jnCommas[1])) ThisForm.AtomArray[i,3]= ; VAL(SUBSTR(jaAtoms[i],jnCommas[2]+1, ; jnCommas[3]-jnCommas[2])) ThisForm.AtomArray[i,4]= VAL(SUBSTR(jaAtoms[i],jnCommas[3]+1, ; jnCommas[4]-jnCommas[3])) ThisForm.AtomArray[i,5]=

VAL(SUBSTR(jaAtoms[i],jnCommas[4]+1, ; jnCommas[5]-jnCommas[4])) ThisForm.AtomArray[i,6]= VAL(SUBSTR(jaAtoms[i],jnCommas[5]+1))ENDFOR

* Populate bond array

DIMENSION ThisForm.BondArray[jnBonds,2]FOR i=1 to jnBonds ThisForm.BondArray[i,1]= ; VAL(LEFT(jaBonds[i],AT(',',jaBonds[i])-1)) ThisForm.BondArray[i,2]= ; VAL(RIGHT(jaBonds[i],LEN(jaBonds[i])- ; AT(',',jaBonds[i])))ENDFOR

Step 2: Scaling the dataThe form’s ScaleData() method handles this task.Chemists will use whatever coordinate system isconvenient for their work. These coordinates have to beadjusted so that the center of the molecule is the center ofthe coordinate system. In our cube example, the data fileis based on one corner of the cube being at the origin(0,0,0); we need to have the center of the cube at (0,0,0)

This is actually quite easy to do: Simply loop throughthe coordinate array to find the largest and smallestvalues for each axis, X, Y, and Z, take the halfway value ofeach, and subtract that from each value. For the X axis ofour cube (column 2), the largest and smallest values are 1and 0. To center the X values, we subtract 0.5 from every Xvalue so that they now run from -0.5 to +0.5.

Many molecules are “lopsided.” Heme, the moleculedisplayed in Figure 1, is flat. To be sure that the largestdimension of the molecule fits in the viewport, find themaximum of each dimension, and scale every coordinateto about 90 percent of the view port height and width.

The last line of code in this method sorts the atomarray based on the Z coordinate, column 4. More onthis later.

local jnMax, x_max, y_max, z_max, ; xCenter, yCenter, nRadius

STORE 0 to x_max, y_max, z_maxSTORE 0 to x_min, y_min, z_min

* This code centers the atoms in their* current coordinate space.for i=1 to ThisForm.jnAtoms * MAX value x_max=MAX(ThisForm.AtomArray[i,2],x_max) y_max=MAX(ThisForm.AtomArray[i,3],y_max) z_max=MAX(ThisForm.AtomArray[i,4],z_max) * MIN Value x_min=MIN(ThisForm.AtomArray[i,2],x_min) y_min=MIN(ThisForm.AtomArray[i,3],y_min) z_min=MIN(ThisForm.AtomArray[i,4],z_min)endfor

jnXCenter=(x_max+x_min)/2jnYCenter=(y_max+y_min)/2jnZCenter=(z_max+z_min)/2

for i=1 to ThisForm.jnAtoms ThisForm.AtomArray[i,2]= ; ThisForm.AtomArray[i,2]-jnXCenter ThisForm.AtomArray[i,3]= ; ThisForm.AtomArray[i,3]-jnYCenter ThisForm.AtomArray[i,4]= ; ThisForm.AtomArray[i,4]-jnZCenterendfor

Page 7: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 7FoxTalk January 2000

* Now find the max of all + and - dimensions.* Some positions may be negative,* so we use the ABS() function.

for i=1 to ThisForm.jnAtoms x_max=MAX(ABS(ThisForm.AtomArray[i,2]),x_max) y_max=MAX(ABS(ThisForm.AtomArray[i,3]),y_max) z_max=MAX(ABS(ThisForm.AtomArray[i,4]),z_max)endfor

jnMax=MAX(x_max,y_max,z_max)

* Then redimension each x,y,z to fit the* drawing area. Get the form properties once* for speed. First, make all of the coordinates* proportional to jnMax.

nRadius=ThisForm.jnRadius && 90 pixels

for i=1 to ThisForm.jnAtoms ThisForm.AtomArray[i,2]= ; ThisForm.AtomArray[i,2]*nRadius/jnMax ThisForm.AtomArray[i,3]= ; ThisForm.AtomArray[i,3]*nRadius/jnMax ThisForm.AtomArray[i,4]= ; ThisForm.AtomArray[i,4]*nRadius/jnMaxendfor

* Place in Z orderASORT(ThisForm.AtomArray,4)

Step 3: Draw the moleculeThe form method DrawMolecule() draws the image; it’sused when the file is first opened, and each time the userrotates the image. But there’s one mathematical changewe have to make. The array holds the molecule’scoordinates centered at 0,0,0. When the molecule isdrawn, the center will be X=100, Y=100, Z= 0, the center ofthe viewport. Moving the center of the molecule is calleda spatial translation. This is part of the drawing process;the coordinates in the array aren’t actually changed.

In geometry, the X axis usually is shown runninghorizontal from left to right. In FoxPro, the form’s Xcoordinate works the same way. To be sure the valuesstart at the center of the viewport, or 100, I just add 100 toeach X coordinate before drawing. The Y axis is usuallydrawn vertically, running from bottom to top. On FoxProforms, the Y axis runs from top to bottom. To get thisright, subtract the atom’s Y value from 100. The Z axis istypically drawn as coming forward out of the page.

The Line() and Circle() methods that draw the atomsand bonds take parameters that are the X and Ycoordinates from the file. These graphic shapes exist intheir own graphics layer and will be drawn on top of anycontrol you place on the form. The CLS() method removesthese images without affecting VFP controls and objects.Since we can only draw in two dimensions, we just ignoreZ. (That is easy!)

There are two tricks to getting the molecule to lookright in the Z dimension. The bonds are drawn first.When the atoms are drawn later, the bonds will look likethey start and stop at the surface of the atoms. Then, theatoms are drawn from back (-Z) to front (+Z). By using theASORT() function on the atom array, the atoms with the

smallest (in this case, negative) values are at the top of thearray and so are drawn first. When the nearer atoms aredrawn, they appear to be in front of the previously drawnatoms. The nearer atoms eclipse the farther atoms andgive a good 3D perspective.

* Graphics drawing routine. This method is called* each time the figure has to be redrawn.

* At this point, the atoms array contains data in* screen coordinates. Each call to the rotation* functions will recalculate/repopulate the array.

* Draw the bonds first.* Loop through the bond array for each atom pair.* Look up the atom's x and y coordinates for each* end, then draw a simple line.

* Note: Ignore Z! Only loop until both matching* atoms are found! Column 1 is the atom number.

* Clear any existing display just before* the draw starts. This has to be before* the lockscreen.

ThisForm.CLSThisForm.lockscreen=.t.IF ThisForm.chkBonds.VALUE=1 FOR i=1 TO ThisForm.jnBonds jlFoundAtom1=.f. jlFoundAtom2=.f. FOR j=1 TO ThisForm.jnAtoms * Find first match IF ThisForm.BondArray[i,1]= ; ThisForm.AtomArray[j,1] jlFoundAtom1=.t. x1=ThisForm.jnxCenter+ ; ThisForm.AtomArray[j,2] y1=ThisForm.jnyCenter- ; ThisForm.AtomArray[j,3] ENDIF * Find second match IF ThisForm.BondArray[i,2]= ; ThisForm.AtomArray[j,1] jlFoundAtom2=.t. x2=ThisForm.jnxCenter+ ; ThisForm.AtomArray[j,2] y2=ThisForm.jnyCenter- ; ThisForm.AtomArray[j,3] ENDIF IF jlFoundAtom1 .and. jlFoundAtom2 EXIT ENDIF ENDFOR ThisForm.LINE(x1,y1,x2,y2) ENDFORENDIF

* Draw circles. We draw these last so* they sit on top of the vertices.* But we have to draw from back to front.* The array has been ASORT()'ed on the* Z column when we get here.* This is done in the X and Y rotation functions.* It's not needed in Z rotation!

IF THISFORM.chkAtoms.VALUE=1 FOR i=1 TO THISFORM.jnAtoms x=THISFORM.jnxCenter+THISFORM.AtomArray[i,2] y=THISFORM.jnyCenter-THISFORM.AtomArray[i,3] r=THISFORM.AtomArray[i,5] ThisForm.FILLCOLOR=THISFORM.AtomArray[i,6] ThisForm.CIRCLE(r,x,y) ENDFORENDIF

ThisForm.lockscreen=.f.

There are check boxes on the form to allow the user to

Page 8: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

8 http://www.pinpub.comFoxTalk January 2000

decide whether he or she wishes to omit either the bondsor atoms in the drawing. The Click() method of thesecheck boxes is as follows:

ThisForm.drawmolecule()

Rotating the moleculeTo rotate the molecule, new coordinates must becalculated based on the current coordinates of each atomand the angle selected in the spinner. On the form, thereare three pairs of command buttons—one pair dedicatedto each axis. The Click() method first calls a method tocalculate the new, rotated position, and then draws themolecule.

ThisForm.x_rotation(ThisForm.increment)ThisForm.DrawMolecule()

The code for the x_rotation method() follows. When arotation is made about the X axis, only the Y and Z valuesneed to be recalculated. For the X axis rotation, the new Yand Z coordinate values are based on the old Y and Zcoordinate values. The old values are stored in localvariables before applying the trigonometric functions.After looping through the atom array, ASORT() the arraybased on column 4, the Z coordinate. And don’t forget—for the Z-axis rotation, omit the ASORT().

* X-Axis rotation:

lparameter jnAngle

local yOld, zOld

for i=1 to ThisForm.jnAtoms yOld=ThisForm.AtomArray[i,3] zOld=ThisForm.AtomArray[i,4] ThisForm.AtomArray[i,3]= ; yOld*COS(jnAngle)+zOld*SIN(jnAngle) ThisForm.AtomArray[i,4]= ; -yOld*SIN(jnAngle)+zOld*COS(jnAngle)endfor

ASORT(ThisForm.AtomArray,4)

The parameter that’s used with these methods,jnAngle, is based on the angular values in the spinner,which I ’ve set for five-degree increments from 0 to 45degrees. But there’s a twist. The FoxPro trigonometryfunctions use values in radians. Most people are morefamiliar with angles measured in degrees. I treat thespinner value as degrees, but in the InteractiveChange()method of the spinner, I convert the angle to radians andstore it to the form property ThisForm.increment. (Oneradian is the angle made by measuring a distance alongthe circumference of a circle equal to the radius of thecircle. If you remember from high school, the formula forthe circumference of a circle is C=2*pi()*r. Thus, there are2 pi radians in the 360 degrees of the circle’scircumference, and one radian is just under 60 degrees.)

ThisForm.increment=this.value*(2*pi()/360)

Finishing touchesThe Click() method of the Reset button simply rereads thedata file.

WITH ThisForm IF !EMPTY(.CboSelect.value) .ReadDataFile(ALLTRIM(ThisForm.CboSelect.value)) .ScaleData() .DrawMolecule() ENDIFENDWITH

Just below the combo box are two labels whosecaption properties are set to the number of atoms andbonds in the data file. The largest molecule in theSubscriber Download file is DIAMOND.MOL, with 95atoms and 128 bonds. This is just a fragment of thecrystalline structure of diamonds. Also included isFULLERENE.MOL, a newly created molecule that lookslike a soccer ball. Its full name is buckminsterfullerene,after its similarity to the geodesic structures ofBuckminster Fuller. I made one of the atoms in thismolecule a different color just to be able to see the amountof rotation.

ConclusionsYou might have noticed that all of the necessary mathfunctions were native FoxPro. Not only were all of the trigfunctions built-in, so was the value of pi and the ability tosort the array on the correct column. It looked as if FoxProwas made to do complex math; in fact, Dr. Dave Fulton,founder of Fox Software, was a mathematician. Nowonder Rushmore is so fast, and FoxPro so mathematical!

I was also pretty amazed by the drawing capabilitiesof FoxPro. While these simple stick-and-ball molecules areprimitive compared to true full-color renderings, FoxProcan draw and manipulate these images in real time. Thismeans the user can have a better sense of his or her dataand perform a lot of data manipulation without having towait for a full rendering. And isn’t understanding data thereal goal of data management?

If you would like to collect other molecularstructures, you should visit these two Web sites:www.molecules.org and www.molecules.com. Bothsites contain numerous molecular files similar to theones used here. ▲

01STONSC.ZIP at www.pinpub.com/foxtalk

Robert Stone is chief of the electronic laboratory reporting section of the

New York State Health Department. He manages several environmental

disease and exposure registries and has developed data-entry software

for several large epidemiological studies. He’s currently working with the

National Institute for Occupational Safety and Health on the collection of

data related to pesticide exposure. [email protected].

Page 9: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 9FoxTalk January 2000

Breath Mint? Candy?No, It’s ASSERTJim Booth

FoxTalk

6.06.0

When designing functions and methods, it’s often necessaryto ensure that good data has been passed and that the ruleshave been followed. Jim Booth reminds us that Visual FoxProhas a tool we can use in this situation called ASSERT.

THE problem of ensuring compliance with thecontracts in certain applications and frameworks isoften a difficult one. You’re coding a method for a

class, and you know that the method wants threeparameters each, of Logical, Numeric, and Character type,respectively. The problem is, how do you ensure that theproper data types and number of parameters are present?

Programming defensivelyA common method of handling this situation is to use“defensive programming,” a technique that attempts toin-line check for errors and prevent them from occurringat all. The following code snippet is for a fictitious user-defined function:

LPARAMETERS plParm1, pnParm2, pcParm3* Here's defensive codeIF VarType(plParm1) <> "L" MessageBox( "First parameter to this routine is " ; + "incorrect data type.", 0, ; "Program Error") RETURN .F.ENDIFIF VarType(plParm1) <> "N" MessageBox( "Second parameter to this routine is "; + "incorrect data type.", 0, ; "Program Error") RETURN .F.ENDIFIF VarType(plParm1) <> "C" MessageBox( "Third parameter to this routine is " ; + "incorrect data type.", 0, ; "Program Error") RETURN .F.ENDIF…

This is an example of defensive code. It checks thedata type of each parameter and displays an errormessage if the rules aren’t followed. However, there’s atleast one problem that I can see in this code—the user willsee the cryptic error messages. This could be fixed bymaking the messages user-friendly, but there’s nothingthe user can do about the problem. Also, a user-friendlyerror message might not convey all of the informationthat the developer needs to fix the problem.

Enter ASSERTVisual FoxPro gives us the ASSERT command. Thepurpose of this command is exactly what we’re talkingabout: to assert that a certain condition is true or todisplay a message. How is ASSERT different fromdefensive programming? Well, in a nutshell, ASSERT canbe turned off.

Using the SET ASSERTS ON/OFF command, you’reable to control whether or not an ASSERT command willexecute. It’s fairly common practice to use SET ASSERTSON during development and then SET ASSERTS OFF forthe deployed application.

Using ASSERT, the preceding code might looklike this:

LPARAMETERS plParm1, pnParm2, pcParm3* Here's ASSERT codeASSERT VarType(plParm1) <> "L" ; Message "First parameter to this routine is " ; + "incorrect data type."ASSERT VarType(plParm1) <> "N" ; Message "Second parameter to this routine is " ; + "incorrect data type."ASSERT VarType(plParm1) <> "C" ; Message "Third parameter to this routine is " ; + "incorrect data type."

The ASSERT commands will each evaluate theirconditions. If the condition is true (.T.), the code willcontinue. If the condition is false (.F.), the message will bedisplayed. The MessageBox used by the ASSERTcommand offers the usual options plus one veryimportant one, Debug. If the developer selects Debugfrom the MessageBox he or she is placed in the VFPdebugger right on the line of code for the ASSERT.

If we SET ASSERTS OFF and run the same code withthe same parameter error, nothing will happen. That’sbecause SET ASSERTS OFF stops the ASSERT commandfrom doing its thing.

Whoa, error checkingthat doesn’t check for errors?How can this be a good thing? Writing code to check forerrors and then turning it off at runtime somehow doesn’tsound right to me. The two approaches can be combinedto provide the ASSERT options that a developer needs aswell as error handling at runtime. In the example code,we could make it like this:

Page 10: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

10 http://www.pinpub.comFoxTalk January 2000

LPARAMETERS plParm1, pnParm2, pcParm3* Here's combined codeIF VarType(plParm1) <> "L" MessageBox( "A program error has occurred. Please " ; + "contact the support line.", 0, ; "Program Error") ASSERT VarType(plParm1) <> "L" ; Message "First parameter to this routine is " ; + "incorrect data type."RETURN .F.ENDIF

IF VarType(plParm1) <> "N" MessageBox( " A program error has occurred. Please " ; + "contact the support line.", 0, ; "Program Error") ASSERT VarType(plParm1) <> "N" ; Message "Second parameter to this routine is " ; + "incorrect data type."RETURN .F.ENDIF

IF VarType(plParm1) <> "C" MessageBox( " A program error has occurred. Please" ; + "contact the support line.", 0, ; "Program Error") ASSERT VarType(plParm1) <> "C" ; Message "Third parameter to this routine is " ; + "incorrect data type."RETURN .F.ENDIF…

This example has both the MessageBox for the usermessages (which has translated the error messages intosomething the user can understand) and the ASSERTcommands that will assist developers during their work.When SET ASSERTS is ON, the developer will first see theuser message and then the ASSERT message. SETASSERTS OFF will suppress the developer messages butstill display the user messages.

It’s not really an errorAnother place that an ASSERT command can be helpful iswhen a function or method is able to provide a defaultvalue for a parameter. The defensive programmingapproach would be used to supply the default value.However, it might still be desirable to display a messageto the developer that a default value is being used. Thiscould alert the developer that he or she has forgottensomething if the default value wasn’t intended to be used.

The following snippet shows the use of ASSERT inthis context.

LPARAMETERS plParm1, pnParm2, pcParm3

* Here's defensive codeIF VarType(plParm1) <> "L"

ASSERT .F. ;MESSAGE "First parameter to this routine is incorrect" ; + " data type. Default will be used."

* Set defaultplParm1 = .F.

ENDIFIF VarType(plParm1) <> "N"

ASSERT .F. ;MESSAGE "Second parameter to this routine is incorrect" ; + " data type. Default will be used."

* Set defaultpnParm2 = 1

ENDIFIF VarType(plParm1) <> "C"

ASSERT .F. ;Message "Third parameter to this routine is incorrect" ; + " data type. Default will be used."

* Set defaultpcParm3 = SPACE(1)

ENDIF…

The above code would discover that a parameter wasthe wrong data type, call the ASSERT every time the IF isfalse unless SET ASSERTS is OFF, tell the developer that adefault value is about to be used, and allow him or her todebug the code if desired. The ASSERT .F. will alwaysdisplay its message, unless SET ASSERTS is OFF.

Asserting the value of ASSERTThe ASSERT command can, and should, be used toenforce compliance with the designed interface to a givenroutine. Whether the routine is a form, a program,a UDF, or a method of a class, the accurate functioningof the routine is dependent on compliance with theinterface rules.

The ASSERT command makes it fairly easy tocheck for this compliance in your code. If you needboth development time and runtime checking, youcan combine defensive programming with ASSERT toprovide both. ▲

Jim Booth is a Visual FoxPro developer and trainer. He has spoken at

FoxPro conferences in North America and Europe. Jim has been a

recipient of the Microsoft Most Valuable Professional Award every year

since it was first presented in 1993. He is coauthor of Effective Techniques

for Application Development and Visual FoxPro 3 Unleashed and is a

contributing author for Special Edition Using Visual FoxPro 6.0. Jim is also

the technical editor for Database Design for Mere Mortals. 203-758-6942,

www.jamesbooth.com, [email protected].

Earn Money!. . . and see y our name in pr int

Go ahead, add a line to your résumé—“Published Articles.”If your tip shows up in the pages of FoxTalk,

we’ll send you $25. See the back page for theaddress where you can send your tips.

Page 11: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 11FoxTalk January 2000

Best Practices FoxTalk

Seeing Patterns:The Factory MethodJefferey A. Donnici 6.06.0

This month’s Best Practices column closes out the “SeeingPatterns” series with an examination of the Factory Methodpattern. The Factory Method design pattern is the secondCreational pattern to be discussed in this series. While it’s nota terribly complex pattern, it does have a few unique traitsthat make it distinctive among the patterns Jefferey Donnicihas discussed so far. When a class is responsible for creatingother objects at runtime, the Factory Method pattern allowsthe subclasses to decide which objects to instantiate.

ONE of the many problems that object-orientedsoftware development is meant to solve is thedifficulty with creating dynamic applications that

change their behavior at runtime. Any number of factorsmight affect a system’s behavior, from user interaction tohistorical data values, and in many cases there’s morethan one factor to be considered at a time. As such, ourapplications have to have a variety of behaviors andfunctionality to address these conditions. Because weencapsulate behavior and functionality into distinctobjects, this typically means dynamically creating anobject based on the runtime needs of the system.

This month’s pattern, and the final pattern in this“Seeing Patterns” series, is the Factory Method. Thispattern helps address this problem of “dynamic creation”by providing a design solution that allows the applicationto create different objects based on the current systemstate. As with the last couple of patterns, the FactoryMethod is a pattern that doesn’t get as much “word ofmouth” in online discussions and printed publications assome of the other, more popular patterns (the Bridge andMediator patterns come to mind). My guess is that theFactory Method pattern, like the Template Method Idiscussed a couple of months ago, is just one of thosepatterns that doesn’t immediately strike an “A-ha!”response from developers who read about it. You’ll learnin this article that there’s really no rocket sciencehappening in designs based on this pattern; it’s a fairlystraightforward pattern that, when you need it, can reallybe a lifesaver.

And for the final time in this series, let me runthrough the essential introductory information about the

“Seeing Patterns” series. The idea behind this series,which started with the November 1998 issue of FoxTalk(“Seeing Patterns: The Bridge”), is to look at object-oriented design patterns individually, with a differentpattern being discussed each month. The thing that makesthese discussions different from most patterns articles inthe object-oriented world is that the patterns arepresented using Visual FoxPro examples and concepts. Allof the examples use Visual FoxPro, and the VFP baseclasses are referred to when a specific type of class isbeing discussed. If this is the first month that you’retuning in for this series, I’d humbly suggest that youcheck out some of the earlier Best Practices columns inthis series. The first few columns in the series containbasic information about the concepts behind object-oriented design patterns, as well as details about thedifferent types, or families, of patterns. The August andSeptember 1999 columns served as a “mid-semester”break, using a question-and-answer format to discusssome of the common questions that have come up in theseries. If this is the first month you’re joining the series,those issues might be helpful in terms of catching up withwhat’s been covered thus far.

Every pattern in this series, including this month’sFactory Method pattern, was initially presented to theobject-oriented development community in DesignPatterns: Elements of Reusable Object-Oriented Software, byE. Gamma, R. Helm, R. Johnson, and J. Vlissides—commonly referred to as the “Gang of Four” or “GOF”(published by Addison-Wesley, ISBN 0-201-63361-2).This book is also available on CD-ROM, including sourcecode examples and hyperlinks between chapters andtopics. As you read any of the columns in this series, youmight find it useful to have the book or CD-ROM nearbyas a reference.

And finally, it’s important for me to point out thatVisual FoxPro is merely the language I’m using todemonstrate the patterns in these series. The patterns,however, are object-oriented design patterns and not VisualFoxPro design patterns. Every pattern in this series can be(and is) used in just about any other object-orienteddevelopment environment. And because an “object-

Page 12: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

12 http://www.pinpub.comFoxTalk January 2000

oriented design pattern” describes a solution to an object-oriented design problem, any implementation examples(code or diagrams) used in this series shouldn’t beassumed to be the only possible implementations ofdesigns based on these patterns. In other words, yourmileage may vary, some restrictions apply, contestrules are subject to change, and this offer not validin Wisconsin.

The Factory MethodAs I mentioned previously, the Factory Method pattern isreally pretty simple to understand, provided that yourecognize the never-underemphasized importance of theinterface in object-oriented programming. By interface, Idon’t mean the more common “user interface” usage,which describes the windows and controls that a userinteracts with while they’re using the application. Instead,I mean the programmatic interface that an object exposes toother objects within the system.

The programmatic interface consists of the membermethods (and those methods’ parameters and returnvalues) that an object uses to interact with other objects. Ifyou’ve made member properties publicly available inyour class definitions, then those properties also become apart of the object’s interface at runtime.

As with the Template Method pattern (see the BestPractices column in the November 1999 issue of FoxTalk—“Seeing Patterns: The Template Method”), the FactoryMethod pattern centers around a single method on anobject’s interface and, more importantly, the “door” thatthat method opens to increased reusability and/orfunctionality. Where the Template Method is a behavioralpattern, therefore addressing runtime object behaviorissues, the Factory Method is a creational pattern.

Creational patterns address some aspect of theinstantiation process wherein a class definition becomesan object within the system. Creational patterns serve to

fundamental assumptions in the Factory Method isthat inheritance will be used, making it a classcreational pattern.

With the Factory Method pattern, the idea is that asingle method handles the creation of certain types ofobjects, providing an instantiated object to the systemwhen it’s finished. Because this method is serving tocreate the object, the purpose of the word “factory” in thepattern’s name becomes immediately obvious. Most of thetime, the Factory Method pattern is used with applicationframeworks or highly reusable and modular components.The component will typically have a method (or morethan one) that creates some other object needed by thecomponent, but the component doesn’t know in advancewhat type of object it will be interacting with. Instead, itcalls the factory method with the understanding that anobject of a certain class-type will be created and eitherreturned or otherwise made available to the component.By expecting an object of a specific class-type, thecomponent is expecting an object that’s at least a certainclass within a class hierarchy; in other words, either aspecific class or some subclass of that class.

By providing the Factory Method pattern, thecomponent is stating that its subclasses should knowwhich class to instantiate. As mentioned previously, thisreliance on inheritance is what makes this pattern one ofthe “class creational” patterns (see Figure 1).

Putting it togetherSo how do factory methods in the subclassed componentsknow which type of class within a class hierarchy toinstantiate? As with many things in Visual FoxPro, itdepends. In truth, the designer (that’s you, by the way)can use a variety of methods to decide which approach touse in any given circumstance:

• You might have an abstract “creator” class that justprovides the basic implementation around the factory

Figure 1. The Factory Method pattern allows a component’s subclass to determinewhich types of objects it needs to instantiate.

abstract the instantiation process sothat it can be used to dynamicallyaffect the behavior of the systembased on the system’s runtime state.As explained in Design Patterns, thereare two types of creational patterns:“A class creational pattern usesinheritance to vary the class that’sinstantiated, whereas an objectcreational pattern will delegateinstantiation to another object.” Priorto this month, the only othercreational pattern to be discussed inthis series was the Singleton pattern(see the May 1999 issue of FoxTalk).While the Singleton is an objectcreational pattern, one of the

Page 13: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 13FoxTalk January 2000

method and leaves the decision over which ancillaryclass to instantiate (as well as how to instantiate it)entirely to the subclass.

• You could use a concrete “creator” class that providesa basic, “default” implementation so that the majorityof situations are covered, and the creator classsubclasses can handle the exceptions.

• Another option is to use some external mechanism fordetermining the class to instantiate, such as aGetUtilityClass or similar method.

• Finally, you might use a parameterized FactoryMethod so that a single “creator” can create multipleutility classes—the indicator for which class toinstantiate comes into the Factory Method as aparameter, perhaps with the resulting object itselfreturned as the return value.

Regardless of which method you choose for decidingwhich class to instantiate, you’ve gained a significantamount of flexibility and reusability by having theFactory Method within the class create the object thecomponent will need at runtime. In doing so, the FactoryMethod serves as a “hook” for subclasses to over-ride toprovide some other, more specialized, version of theclass’s functionality (at least with regards to its associativerelationship with other objects at runtime).

They’re everywhere!As you’ve probably gathered by now, the Factory Methodpattern is really a fairly small pattern, in terms of what itsimplementation looks like. Unlike other patterns, such asthe Mediator or Bridge, the Factory Method and TemplateMethod patterns are both relatively simple to “see inaction.” For an example of the Factory Method, I’ll turnone last time to the Codebook application framework(from the book/CD combination, The Visual FoxPro 3Codebook, by Y. Alan Griver, published by Sybex). Ifyou’ve been following along for awhile, I suspect you’veprobably picked up an old copy by now to see what allof the fuss is about. When the Visual FoxPro communitywent object-oriented, nobody was ahead of the Codebook framework authors when it came tounderstanding and applying many of the more advancedobject-oriented techniques.

At the core of the Codebook framework is thecApplication class, which is the abstract class from whichany Codebook application object is subclassed. Becausethe cApplication class is a subclass of the cChildCollectionclass, it has a LoadChildren method that adds a variety ofmember objects to the application object (the baseclass forthe collection classes is Container). For more details onhow this works, see the Template Method discussion inthe November 1999 Best Practices column. One of theobjects that’s added as a member, or child, object is the

oToolbars object, a collection of the application’s toolbars.This is specified by the LoadChildren method in thepopulation of the aChildren member array:

laChildren[2, CHILD_CLASS] = "CToolbarCollection"laChildren[2, CHILD_NAME] = "oToolbars"

An instance of the cToolbarCollection is created,given the name oToolbars, and added as a member objectto the application. With that in place, the application has aplace to “hang” the toolbars that it might need during thelifetime of the application.

But how does it know which toolbar to createinitially? Enter the Factory Method pattern.

While the process for creating a toolbar can remainconsistent from one application to the next, especiallygiven the strong toolbar base classes provided, thedecision over which toolbar to create is left to thesubclasses—that is, those classes that representactual applications.

Within the cApplication class, there’s a Do method—this method provides the fundamental wait state for anyCodebook-based application. This is where the READEVENTS for the application occurs, and there’s not muchfor that method to do but “run” the core application. Priorto the Do method being called, the developmentenvironment settings have been saved, and differentsettings have been changed as required for theapplication. This leaves the basics for the Do method:

THIS.ShowMenu()THIS.ReleaseSplash()THIS.ShowToolbar()*-- Shutdown handler omitted for clarityREAD EVENTS

In total, there are fewer than 10 lines of code in the Domethod, but our focus for this discussion is on theShowToolbar method. It’s there that we can see theFactory Method implemented in a clear, concise fashion.This method simply adds the toolbar object to theoToolbars collection previously created. Again, the meansof adding the toolbar is consistent for all subclasses ofcApplication, but each application must know whichtoolbar to create as part of its initial startup.

Here are the relevant lines of code from theShowToolbar method:

IF NOT EMPTY(THIS.cMainToolbarClass) *-- UI message call omitted for clarity IF THIS.oToolbars.Add(THIS.cMainToolbarClass) THIS.oMainToolbar = ; THIS.oToolbars.Get(THIS.cMainToolbarClass) THIS.oMainToolbar.Show() ENDIFENDIF

Notice the very first line of code—the method ischecking to see whether a property of the application

Continues on page 17

Page 14: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

14 http://www.pinpub.comFoxTalk January 2000

FoxTalk

Persistence Without PerspirationDoug Hennig

In this month’s article, Doug Hennig presents a set of classesthat persist values. A common application of this technique isrestoring a form’s last size and position when it’s opened.

HAVE you ever had a client ask you why the formsin your application always come up in the centerof the screen (or worse, in the upper-left corner)

rather than where they were when the form was closed?Want to do something about it? This month, we’ll look atclasses that allow you to persist (a $5 word meaning saveand restore) values, such as form size and position.

In this article, I use the term “item” (for want of abetter generic term) to mean the thing we’re persistingvalues for. An example of an item is the Top property of aform. The persistent class we’ll look at in a momentallows you to specify which items the persistent object issupposed to manage (meaning that we’ll save and restorethe values for it, either automatically when the object iscreated and destroyed or manually by calling methods).

Before we get into the class, I’ll discuss a storagemechanism for persisted values. There are lots of placeswe can store values: in a table, in a text file (such as an INIfile), in the Windows Registry, and so forth. However, youcan probably see that while the actual code for readingand writing values varies with the storage mechanism,the overall logic of persisting values (looking up all of thevalues of the items we’re managing somewhere andrestoring them, and doing the opposite for saving) is thesame. So, we’ll first look at a class that manages theabstract behavior of persistence.

SFPersistentSFPersistent (located in SFPERSIST.VCX) is the base classof persistent objects. It’s an abstract class and can’t beused directly, because while it defines the interface forpersistent objects, it doesn’t do any persisting itself.SFPersistent is based on SFCustom, our Custom base classin SFCTRLS.VCX.

The Init method of SFPersistent calls the DefineItemsmethod to specify which items we’re supposed to manage(I’ll discuss DefineItems in a moment). Then, if Init ispassed .F. (or nothing) and the lRestoreOnInit property is.T. (the default), it calls the Restore method to restore thepersisted values of the items we’re managing. Here’s thecode for Init:

lparameters tlNoRestorewith This

* Call the method where the items to be managed are* filled in.

llReturn = .DefineItems()

* If we're supposed to, restore the items managed by* the object.

if llReturn and not tlNoRestore and .lRestoreOnInit .Restore() endif llReturn ...endwithreturn llReturn

The advantage of calling Restore from Init is that youcan simply instantiate an SFPersistent subclass (one thatspecifies how to persist values) or drop it on a form, and itwill restore any saved values for the items it managesautomatically. However, there are times when you mightnot want this behavior, such as if the items it managescan’t be restored automatically (perhaps they’reproperties of an object that doesn’t exist yet). So, you caneither pass .T. to the Init method or set the lRestoreOnInitproperty to .F. The reason for having two ways to do thisis that you could drop an SFPersistent subclass on a form,in which case nothing is passed to Init, or you couldinstantiate it using CREATEOBJECT(), in which case youdon’t have a chance to set lRestoreOnInit until after theInit method fires.

Destroy does just one thing: If the lSaveOnDestroyproperty is .T. (which it is by default), it calls Save to storethe values of the items we’re managing.

DefineItems is an abstract method in SFPersistent; it’sthe place where you can specify what items a subclass orinstance of SFPersistent will manage. This method shouldspecify these items by calling the AddItem method,passing it the name that the item’s value will be storedunder in whatever storage mechanism we’re using, thename of the item (often a property of a form or some otherobject), and optionally the data type of the item. If thedata type isn’t passed, AddItem will use TYPE() to figureit out. However, if TYPE() would fail (for example, you’remanaging properties of an object that hasn’t been createdyet), be sure to pass the data type as the third parameter.Here’s an example of how AddItems is called; this code,taken from the DefineItems method of theSFPersistentForm class (which I’ll discuss later), specifiesthat we’ll manage the Left, Top, Height, and Width

6.06.0

Page 15: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 15FoxTalk January 2000

properties of the form the object is on:

with This .AddItem('Left', 'Thisform.Left') .AddItem('Top', 'Thisform.Top') .AddItem('Height', 'Thisform.Height') .AddItem('Width', 'Thisform.Width')endwith

The Restore method restores the persisted values forone or all items; pass the name of an item to restore thatitem’s value or nothing to restore values for all manageditems. This method calls the protected RestoreOnemethod to restore the value of a single item. RestoreOne isan abstract method because the exact means of restoringan item’s value will depend on the storage mechanism.

lparameters tcItemlocal llReturn, ; lnIwith This

* Ensure that if the property was specified, it's a* valid name, and that we have some properties defined.

if pcount() = 1 and (vartype(tcItem) <> 'C' or ; empty(tcItem)) error cnERR_ARGUMENT_INVALID return .F. endif pcount() = 1 ... if alen(.aItems, 2) = 0 or empty(.aItems[1, 1]) or ; empty(.aItems[1, 2]) error 'Items were not defined' return .F. endif alen(.aItems, 2) = 0 ...

* If no item was specified, restore them all.

llReturn = .T. if pcount() = 0 for lnI = 1 to alen(.aItems, 1) llReturn = llReturn and .RestoreOne(lnI) next lnI

* If an item was specified, try to find it. If we can,* restore it. If not, give an error.

else lnI = ascan(.aItems, tcItem) if lnI > 0 lnI = asubscript(.aItems, lnI, 1) .llReturn = RestoreOne(lnI) else error cnERR_PROPERTY_NOT_FOUND, tcItem llReturn = .F. endif lnI > 0 endif empty(tcItem)endwithreturn llReturn

The Save method, which saves the value of one or allitems, is very similar to Restore, except it calls theprotected SaveOne method. Like RestoreOne, SaveOne isan abstract method.

SFPersistentRegistrySFPersistentRegistry (also located in SFPERSIST.VCX) is asubclass of SFPersistent that persists item values in theWindows Registry. Rather than reading from and writingto the Registry directly, it uses the services of a class thatknows how to do that: SFRegistry, a subclass of the

FoxPro Foundation Classes (FFC) Registry class. Ipresented the SFRegistry class in my article, “Mining forGold in the FFC,” in the December 1998 issue of FoxTalk.

SFPersistentRegistry has an oRegistry property intowhich the Init method instantiates an SFRegistry object.You can pass the Init method the Registry key that will beused for items the object manages; you can also set thecKey property to the desired key (as with turning offautomatic restoration, there are two ways you can set thekey since there are two ways you can instantiate anobject). Use something like “Software\Your CompanyName\Application Name\Object Name” for the key; bydefault, SFRegistry uses HKEY_CURRENT_USER as thetop Registry node (which I’ve sometimes heard referred toas “hive”). The ReleaseMembers method of the class(called when the object is destroyed) sets oRegistry to.NULL. so object references are cleaned up.

The RestoreOne and SaveOne methods ofSFPersistentRegistry are where the mechanism forpersisting is defined. RestoreOne ensures that cKey hasbeen filled in, then uses SFRegistry’s GetRegKey methodto read the value for the specified item from the Registry.If GetRegKey returns .NULL. (meaning an error occurredor, more likely, the value didn’t exist in the Registrybecause it hadn’t been saved yet), the item’s value isn’tchanged. ConvertCharToType is used because SFRegistrycan only store character values in the Registry, so we needto convert the character string read from the Registry backto the correct data type for the item. Also, the value isn’twritten to the item unless it’s different than the item’scurrent value; this prevents an automatic method (such asResize when Top is changed) or an Assign method ofthe item from firing needlessly. Here’s the codefor RestoreOne:

lparameters tnItemlocal lcItem, ; luDefault, ; luValuewith This

* Ensure we have a valid key.

if empty(.cKey) error 'You must specify the cKey property.' return .F. endif empty(.cKey)

* Use the oRegistry object to get the value, convert* it to the proper data type, and store it in the* bound item.

lcItem = .aItems[tnItem, 2] luDefault = evaluate(lcItem) luValue = nvl(.oRegistry.GetRegKey( ; .aItems[tnItem, 1], .cKey), luDefault) luValue = .ConvertCharToType(luValue, ; .aItems[tnItem, 3]) if not luDefault == luValue store luValue to (lcItem) endif not luDefault == luValueendwith

Page 16: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

16 http://www.pinpub.comFoxTalk January 2000

SaveOne is almost identical to RestoreOne, except ituses SetRegKey to write the item’s value to the Registryafter using ConvertTypeToChar to convert it to a string.

To use SFPersistentRegistry, drop it on a form, set thecKey value to the Registry key where values should bestored (for example, “Software\Stonefield Systems GroupInc.\Test”), and put some code in the DefineItemsmethods to specify what items are to be persisted. You canalso instantiate a subclass of SFPersistentRegistry that hasDefineItems filled in; in this case, it’s likely you’ll want tovary the key used from instance to instance, so pass thekey as the first parameter.

SFPersistentFormSFPersistentForm is a subclass of SFPersistentRegistrythat specifically handles form size and position. It simplyhas the code in DefineItems I showed earlier to managethe Top, Left, Height, and Width properties of the formit’s on.

SFPersistentForm is really easy to use: Just drop it ona form and set cKey to the desired key. If you want to dothis generically (that is, dropping it on a form class),you’ll likely want to vary the key with the name of theapplication and form, so in that case, set thelRestoreOnInit property to .F., and in the Init of the form,set the cKey property and call the Restore method. Here’san example:

This.oPersist.cKey = 'Software\My Company\' + ; oApp.cAppName + '\' + This.NameThis.oPersist.Restore()

This code assumes an application object (oApp) has acAppName property that contains the application name,and that the name of the form is part of the key.

TEST1.SCX is a very simple example of usingSFPersistentForm. It has a few controls and anSFPersistentForm object with cKey set to“Software\Stonefield Systems Group Inc.\Test.” Run thisform, move and resize it, then close it. Run it again, andyou’ll see that it opens with the same size and position ithad when you closed it.

You might notice that as you resize the form, thecontrols on the form don’t resize. In the same December1998 column where I discussed SFRegistry, I presented asubclass of the FFC _Resizable class called SFResizable.This class resizes controls when a form is resized; yousimply specify which objects should be resized and how,by filling in properties of the object (see the article fordetails), and call the AdjustControls method in the form’sResize method. TEST2.SCX is a copy of TEST1, but it hasan SFResizable object added to it and a call toThis.oResizer.AdjustControls in Resize. When you runthis form, you’ll see that it opens at the same size andposition as TEST1 (since they use the same Registry key),

but when you resize it, the controls adjust as necessary.However, when you close and rerun the form, it opens atthe last saved size and position, but the controls don’t.Why is that?

It turns out to be related to the order of instantiation.Since the SFPersistentForm object was added to the formfirst, its Init fires first and resizes the form to its formersize. Then, the SFResizable Init fires, and it stores theform’s Height and Width to “initial” properties. Theproblem, of course, is that the form isn’t its original sizeanymore. So, the controls don’t get adjusted correctly.

One solution to this problem would be to ensure thatthe SFResizable object is added to the form first, but thatmeans the behavior of the form depends on the order ofinstantiation of objects, which should set off alarm bellsfor you. Instead, since SFResizable and SFPersistentFormneed to work together at form instantiation, I created asubclass of SFResizable called SFPersistResizer (inSFCCTRLS.VCX in the Subscriber Download file atwww.pinpub.com/foxtalk) that adds an SFPersistentFormobject to itself so the two can work together in the orderthey need to. I added lRestoreOnInit, lSaveOnDestroy,and cKey properties, and a Restore method that simplycalls This.oPersist.Restore to the SFPersistResizer class;we don’t have multiple inheritance in VFP, so I did this so

Page 17: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 17FoxTalk January 2000

the form can just talk to these properties and method ofthe SFPersistResizer object rather than having to addressthem as Thisform.oPersistResizer.oPersist.<property ormethod>. Thus, in addition to providing its own behavior,SFPersistResizer is a wrapper for SFPersistentForm.

The Init method of SFPersistResizer does its basebehavior first (saving the form Height and Width), thenadds an SFPersistentForm object and passes it the valuesof the cKey and lRestoreOnInit properties. Finally, iflRestoreOnInit is .T., it calls the Resize method of the formbecause Resize didn’t fire automatically when the Init ofthe SFPersistentForm object adjusted the form size; Resizedoesn’t fire unless the form has finished instantiating.Resize will call the SFPersistResizer’s AdjustControlsmethod, which will adjust the controls. Thus, once the Initmethod of SFPersistResizer is done, the form and itscontrols have been properly adjusted.

The Destroy method of SFPersistResizer sets theSFPersistentForm object’s lSaveOnDestroy property to itsown property, so when that object gets destroyed, it willsave the form size and position if desired.

To see SFPersistResizer in action, run TEST3.SCX.This form is a copy of TEST2.SCX, but I removed the

The Factory Method . . .Continued from page 13

SFPersistentForm and SFResizable objects and replacedthem with SFPersistResizer. When you run this form,you’ll see that it exhibits the desired behavior: Not onlyis the form size and position restored, so are those ofthe controls.

ConclusionAlthough I didn’t create one, you could easily subclassSFPersistent to use tables or INI files as the storagemechanism. Regardless of which mechanism you use,these classes make persisting values very easy to do. ▲

01DHENSC.ZIP at www.pinpub.com/foxtalk

Doug Hennig is a partner with Stonefield Systems Group Inc. in Regina,

Saskatchewan, Canada. He’s the author or co-author of Stonefield’s add-

on tools for FoxPro developers, including Stonefield Database Toolkit,

Stonefield Query, and Stonefield Reports. He’s also the author of The

Visual FoxPro Data Dictionary in Pinnacle Publishing’s The Pros Talk Visual

FoxPro series. Doug has spoken at the 1997, 1998, and 1999 Microsoft

FoxPro Developers Conferences (DevCon) as well as user groups and

regional conferences all over North America. He is a Microsoft Most

Valuable Professional (MVP) and Microsoft Certified Professional (MCP).

[email protected].

class, cMainToolbarClass, is present. It’s there that ourfactory method, ShowToolbar, determines in the subclasseswhich toolbar class to instantiate. Assuming that thecMainToolbarClass property is populated with a stringcontaining the name of a toolbar class definition availableto the application, that toolbar becomes the main toolbarfor the system.

From there, the method proceeds to tell the oToolbarscollection to add an instance of that class to itself. If thatprocess succeeds, the method gets an object reference tothat new toolbar and assigns it to its own oMainToolbarobject property—this allows the application and its maintoolbar, presumably the toolbar referenced most often bythe application, to communicate directly via that objectreference. Finally, the method calls the Show method tomake the toolbar visible to the user.

Ta-da! The Factory Method pattern really is thatsimple. While there are probably dozens of variations onthis particular implementation, the motivating principlebehind this pattern is present. The basic idea is to providea method in a superclass that will create some sort ofutility object that the overall component will need atruntime—the key, however, is that the class defers to itssubclasses to decide which class to instantiate and how

that class should be instantiated.

Whew!Well, that wraps up the Best Practices “Seeing Patterns”series. Hopefully, I’ve achieved the goals I outlined whenthe series started more than a year ago—to discuss object-oriented design patterns in a way that’s familiar to VisualFoxPro developers and to dispel some of the myths or“black magic” surrounding design patterns. I know that,by virtue of writing about them, I’ve gained quite a bitmore understanding of how to really use these patterns inmy own work. With any luck, some of you have gainedsimilar insights or otherwise found the past year’s worthof Best Practices columns to be worthwhile in your object-oriented design work.

Next month, the Best Practices column returns to“normal” (Yeah, right!—Ed.), with the usual fare of topicschosen to separate the hackers from the programmersfrom the professional software developers. As always, feelfree to let me know if you have any suggestions orcomments for this column. ▲

Jefferey A. Donnici is the senior Internet developer at Resource Data

International, Inc. in Boulder. Jeff is a Microsoft Certified Professional and

a five-time Microsoft Developer Most Valuable Professional. 303-444-

7788, fax 303-928-6605, [email protected],

[email protected].

Page 18: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

18 http://www.pinpub.comFoxTalk January 2000

Page 19: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 19FoxTalk January 2000

The Kit Box FoxTalk

Easy as Falling off a LogPaul Maskens and Andy Kramek 6.06.0

In this issue, Paul Maskens and Andy Kramek discuss audittrails, logging, rollback, and roll-forward.

Paul: Okay, so we’ve decided to log changes to our tables.We need to find the best way to do it.

Andy: Why?

Paul: What do you mean, why? To write the most efficientcode, of course.

Andy: No, I mean why do you want to log changes?

Paul: Initially, so that we can track changes to the data anddetermine what changed, and when. We’d also be able totrack who (or what program) made the change. But whyis the reason for logging important?

Andy: Well, the reason for logging changes determineswhat you need to track, and how you set about doing it.

Paul: Hmm, I agree that some techniques I’ve seen don’tdo what we need, so yes, I take your point.

Andy: If you want to log “who did what, when,” you’llalso need to record the kind of action taken (Insert,Update, Delete) and the data that was affected, as well asyour user information.

Paul: Right, I suspect that in reality this isn’t so muchabout auditing, but more about being able to apportionblame. What was originally specified (by the users’manager) was to store Date, Time, Program, and UserIDfor the last change on each each record.

Andy: For history and analysis of changes, that’sinsufficient. You need to log the changes to the data aswell, and if you want to be able to roll back changes to aprior state, restore, and roll-forward—well, you’ll need avery different solution.

Paul: I take your point, and for once I agree. I’ve seen thisDate/Time/ID approach in other people’s systems too,dating right back to FoxPro 2.5. It has several drawbacks:there’s no history of the changes, there’s no record ofwhat was changed, and, for frequently accessed tables,you can’t even point the finger of blame at the right

person. In implementation, there’s a problem, too. A VFPtrigger can’t update the record being inserted or updated.That forces you to pass all data actions through a commonprocedure that sets the date, time, and ID.

Andy: Well, that last point isn’t entirely a bad thing. If youhave an n-tier architecture, then your data access is likelyto be through a common data handler. You can add thenecessary fields to any insert, update, or delete whendoing it that way, anyway.

Paul: Point taken. But what if the requirement includesrecording the program making the change? Whichprogram is actually making the change? How can the datalayer know which program ought to be recorded as theprogram for logging purposes? I guess you won’t like theidea of a global variable <g>.

Andy: That’s nonsense, literally, in this case—it doesn’tmake any sense! The program that makes the change isthe program that writes the data.

Paul: As I see it, the program that writes the data isdelegated the mechanical task of writing such data from ahigher level, but what we need to record is the programthat delegated that task. In a layered architecture, thatmight be two or more layers away from the data layer.

Andy: Well, it could be a parameter that gets passed fromlayer to layer. Or a global variable. Or a property of anobject (but you know how I feel about god objects). Ifyou’re logging from a trigger, then it doesn’t make muchsense to have the trigger work out which is the controllingprogram, then simply pass that to the audit routine.

Paul: I’d opt for a state object to store all those kinds ofthings—UserID, Workstation Name, ControllingProgram—so that when you have a program that requireslogging, it sets the ControllingProgram property.

Andy: Okay, but what are you actually going to log? Arewe going to take this beyond the simple per-record log sothat it can actually do something useful, too?

Paul: I think so. We should definitely create a separateaudit trail, at least. The data can be related to the sourcetables by using the primary key of the affected record—

Page 20: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

20 http://www.pinpub.comFoxTalk January 2000

because of course everyone uses primary keys. One thingoccurs to me immediately: What do we do about loggingthe changes to memo fields?

Andy: Don’t.

Paul: What? But, er, well, we want to!

Andy: Do you really? Do they hold critical data?

Paul: Not in this application, no.

Andy: Then it’s not worth the pain.

Paul: If you’re logging in VFP tables, I agree. I assume thatyou’re basing that on the fragility of memo fields and thememo field bloat. In back-end products, that might notbe applicable.

The simplest way, to me, seems to be writingeverything twice. I know it will take more storage, but it’sa no-brainer to implement, right?

Andy: It certainly should be. You’ll get the exact status—who, what, and when. You get the history since multiplecopies of the record exist—note that your primary key isnow not primary anymore. Retrieval of the history of arecord is easy; there’s no reconstruction to do.

Paul: But there’s massive redundancy. For example, if youchange one field, it inserts a whole new record. If fewfields are changing in a large record, then this is veryinefficient. You could end up needing a lot of disk space!

Andy: Of course, this is a brute force approach. But if theaudit tables are not long-lived this might be the quickest(and therefore cheapest) solution to the problem.

Paul: Yes, you’ve made that point again—why we want tolog it and what the log is for are important in determiningthe best solution. So if we want to keep this audit trail fora long time or if we’re short of disk space, how can Iimprove it?

Andy: Come on, you can “do data” if you try—thinkabout it!

Paul: How about this, then: Write one record for eachchanged field. For whole-record operations (Insert,Delete), it’s going to be less space-efficient. But I think thetrade-off will be worth it for updates. It ends up being anumber of small records instead of one large one, andhopefully saving some space.

Something like TableName, PK, FieldName, OldVal,NewVal, Date/Time, UserName, TxnType.

Andy: You’re thinking of actually using a structure

like that?

Paul: Yes, exactly. But your tone of voice says I haven’tgrokked something.

Andy: Well, yes, you’re storing the same data in manyrecords: Date/Time, UserName, TxnType. I don’t want togo into a normalization debate—let’s just say I’m right.

Paul: Okay, okay, I was being lazy. I’d better go back anddo it again. Ummm, let’s see. Header/detail, right?

Andy: Yes. Now how are you going to determine thechanged fields?

Paul: That’s easy—use GETFLDSTATE(-1)—which returnsa string made up of the state of the record’s deleted flagfollowed by the state of every field. The values for anexisting record are Unchanged (1), Changed (2); for a newrecord, Unchanged (3) and Changed (4).

Andy: Except at EOF(), when GETFLDSTATE() willreturn .NULL.

Paul: That makes sense, because at the end of file there’sno record, so it can’t have a field state.

Andy: Another “gotcha”! Any default values that areadded when you insert a new record will also show up aschanged (Value = 4). VFP can’t distinguish between aninserted record with a default value and one created usingAPPEND BLANK and REPLACE.

Paul: Also, if a value is changed and then changed back toits original value, it’s still reported as “changed.” Isuppose you could use SETFLDSTATE() to change 2 to 1or 4 to 3. Would you want to audit a change if the fieldhad been edited to its original value?

Andy: You might, but it’s certainly the easiest approach tojust leave it alone.

Paul: Okay, so we’re going to write one record for eachchange to a transaction table, and possibly many recordsto the detail table.

Andy: I want to mention one more thing: Because thereisn’t a copy of the record anymore, and the types of theOldValue and NewValue will differ from field to field,we’ll need to convert all of the data that we want to writeinto our detail table to character format.

Paul: Right—a common function named something likeExpToStr(), I suppose.

Andy: Well, I named it Con4Log:

Page 21: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 21FoxTalk January 2000

********************************************************** Function to return any value as a character string*** Used to write values to the Audit Log*******************************************************FUNCTION Con4Log( tuExp, tcType )*** Convert the passed expression to stringLOCAL lcRetVal, lcType*** If no type passed -- map to expression typelcType = IIF( TYPE( 'tcType' ) = 'C' ; , UPPER( ALLTRIM( tcType ) ) ; , TYPE( 'tuExp' ) )*** Convert from type to charDO CASE * Integer CASE INLIST( lcType, 'I', 'N' ) ; AND INT( tuExp ) = tuExp lcRetVal = ALLTRIM( STR( tuExp, 16, 0 ) ) * Numeric / Currency CASE INLIST( lcType, 'N', 'Y' ) lcRetVal = ALLTRIM( PADL( tuExp, 32 ) ) * Character CASE lcType = 'C' lcRetVal = ALLTRIM( tuExp ) * logical CASE lcType = 'L' lcRetVal = IIF( !EMPTY( tuExp ), '.T.', '.F.') * date CASE lcType = 'D' lcRetVal = ALLTRIM( DTOC( tuExp ) ) * DateTime CASE lcType = 'T' lcRetVal = ALLTRIM( TTOC( tuExp ) ) OTHERWISE *** There is no otherwise unless, of course, *** Visual FoxPro adds a new data type. In this *** case, the function must be modified lcRetVal = 'Invalid Type'ENDCASE*** Return value as characterRETURN lcRetVal

Paul: When creating the audit detail table, I’m worriedabout the length of the fields. With long fieldnames, wepotentially need 128 characters for the fieldname. For longdata fields, we potentially have a problem: Do we use 255character OldValue and NewValue fields?

Andy: Well, it depends on your purpose. If you’re usingthis as a transaction log so you can roll back the table,then yes. However, I really don’t think building atransaction log for VFP makes sense—just use a capableback end. Do you really hold critical data in longcharacter strings?

Paul: I see what you mean—even Name and Addressfields are rarely longer than C(30)—anything else isprobably “comments” and not critical data. Okay, let’s justsay we’ll only save the first 30 characters. We’re reallyinterested in numbers, dates, names, and addresses,anyway. However, the maximum in a working applicationmight be 40 or even more, depending upon needs.

Andy: It would be better in Oracle using Varchar2 or SQLServer using Varchar data types, which aren’t fixed-length.This can save a lot of space!

Paul: In VFP, we could use memo fields; after all, withblocksize set to 1 the overhead is only 9 bytes. But is itrobust? Nope!

Andy: One thing you really want your audit log to be is robust!

Paul: Okay, now back to the length issue. Typically, ournormalized data stores codes and primary and foreignkeys. Descriptions are static and retrieved via lookuptables. Why store a description in a record? Just why arethe descriptions in the tables? Purely for performance—deliberate denormalization. (Deliberate, we hope, ratherthan just ignorance <g>).

Andy: Do you really want to audit everything in thesystem? If so, do you always need to do it the same way?Possibly name and address history could be tracked in adifferent way. Of what relevance is it to know whatsomeone’s address was six months ago?

Paul: I can think of things like tracking of fraud and creditcontrol. Both might be related to address as well as toindividuals living at that address—it has certainly provenuseful to track both.

Andy: As I see it, there are two determinants. First, thefrequency of change: How often do people change nameand address? In the life of a typical application that storesnames and addresses, how many address changes willthere be? Maybe you could adopt the duplicate-recordapproach for this type of data.

Second, there’s the criticality of the data. It’sobviously important to know whether someone haschanged the amount on the check that you’re about tosend out, but it might not be quite so critical to knowthat the postcode has been corrected by changingone character.

Paul: But those are really design issues.

Andy: But if we can’t make these decisions, it means thatwe’ll have to make it data-driven. Personally, I quite likethe idea of a control table (if only because most peopledon’t use data classes and you don’t have any other wayof doing it that makes sense in that case).

Paul: Hang on, there are other ways. For example, in thecomment property via DBGetProp() and DBSetProp()—but that ruins the comments. How many people (otherthan you) actually use the comment field in the DBC?

Andy: Well you do, but I see what you mean.

Paul: You could use the USER field in the .DBC record forthe table to store the logging type. But then how do youget to it?

Andy: You have to USE my.DBC AGAIN SHARED and

Page 22: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

22 http://www.pinpub.comFoxTalk January 2000

seek to the table’s record. Oh, there’s no single index.You’d have to use ObjectType = “Table” and ObjectName= “mytable” to find it. But then you could get at the Usermemo field. It’s not very clean, but it’s a possibility.

Paul: Of course if you’re using custom cursor objects, youset it as a property of the cursor instance. How aboutlLogTable or nLogType (values 0,1,2)? In VFP6, you caneven use AddProperty() to add properties to the standardcursor, and make that part of your BuildDE() code.

Andy: Actually, I’d probably add a cLogFields property tomy cursor class. This would take a comma-separated listof the fields from the cursor that are to be logged. Fromthere on in, it’s the responsibility of the behavior managerto do the actual logging.

Paul: So now that we’ve decided what to write to the log,how do we do it?

An obvious answer is to write explicitly in thedoSave(), doAdd(), and doDelete() methods. This is fine ifyou want to log key items only, where logging isn’t amajor part of the application or is just not neededeverywhere. This is most useful when logging only one ortwo tables that are updated from a very limited number ofplaces (cash payments, for instance).

We could create a logging class and call methods of itwhen required, or even a family of logging classes anduse polymorphism. Perhaps also a strategy driven by thenLogType—suitable where it’s a more significantrequirement of the application but still for particulartables only. It’s most appropriate when several screensand/or tables require logging, but still affecting only aminority of the data.

Another possibility is to delegate the responsibility tothe Database and use Insert, Update, and Delete triggers.(You can’t make changes in the record you’re inserting,updating, or deleting, but you can in another table.) Thisis the most appropriate approach when most, or all, tablesrequire logging or when one table can be updated fromseveral screens.

Oh, yes, and the table can delegate to the loggingclass, too. Just add a call to the logging class in your RItriggers...or not, as appropriate.

Andy: Not only that, but using triggers has one big benefitover delegating to the class. You can’t rely on the classeswhen someone is using your tables through ODBC. If youwant to audit changes, and you really need to auditchanges made by anyone, anywhere—then use triggers.

Paul: Good point—I hadn’t thought about that! We’ll beusing a Transaction, obviously. It all has to be handled asa single unit that succeeds or fails in its entirety. First, youdon’t want to write to the transaction header if writing to

the detail fails. But you can’t do that until you have theheader’s primary key to put in the detail’s foreign key. Dowe roll back the change of data if the audit fails? Clearlyyou must!

Andy: Not only that, but you must get the sequence ofupdating correct. First you must attempt the change to thetable (if that fails, we’re not going to do any logging); nextmust come the audit header because you need theprimary key to insert into the detail records; finally, youwrite the actual detail. Only if all three succeed do wethen commit the changes.

Paul: This comes back to three things—“design, design,and nothing else.” (A. Kramek, frequently.)

Andy: There are a number of other issues associated withall this. First, there is a performance penalty. This can bevery significant in high-activity tables.

Paul: Ah, but that’s back to our reason for logging in thefirst place. You have to make intelligent choices.

Andy: Second, how long do you keep your logs? Forhistory, never delete everything? For blame, until thatperson leaves? After six months? Purge them when theDBA screams about your multi-megabyte log files? Clearthem after the daily or weekly backup?

Paul: It depends (standard consultant’s answer <g>). It’s aserious problem and it comes back to your first point—why are you logging in the first place? While we’re onthe subject, how are we going to handle Roll backand RollForward?

Andy: It’s a nightmare for anything but the simplest oftables. As soon as relationships are involved, then itbecomes a major undertaking. What’s involved is a wholeapplication for each function, at least in complexity. It canrapidly get out of hand. Use a back end that supports itand don’t reinvent the wheel; it’s not worth it. Let’s juststick to the maintaining of the history.

Paul: How do you reconstruct the history? How do youmanage it if it involves relationships?

Andy: It’s a similar issue to rollback/forward.Synchronizing changes across multiple tables is aproblem. It limits the usefulness of the log. You have tofind the current record for the master table and all of itschildren. You then have to find in the audit log the firstchange to any of those tables and reconstruct the record inthat table for that change. You then have to map backthrough the child and master tables to get the appropriaterecords related to the changed records.

Page 23: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 23FoxTalk January 2000

ConclusionAs with many Internet technologies, PDA databases are astory of “two steps forward, five steps back” when itcomes to functionality. The price you pay for additionaldeployment capabilities—being able to take your datawith you in your shirt pocket and have it be availableinstantly (as opposed to a four-minute boot-upsequence)—is limited functionality: primitive relationalability and small capacities. Next month, I’ll walk youthrough the step-by-step instructions to install andimplement a Palm PDA database, and hook it up with acorresponding Fox database. ▲

01EDITOR.ZIP at www.pinpub.com/foxtalk

Downloads

Paul: You could do it by writing intermediate record sets,writing the current record and all related records whetherchanged or not.

Andy: It obviously can be done. (In fact, I’ve done it.) Butonce you get multi-table relationships, you’re looking atouter joins across three or maybe four tables with a hugeoverhead (Cartesian products). FoxPro choked on it, andthe task had to be performed on the back end. Remember,there is no join condition.

When you get right down to it, the only thing you cansensibly log is information about a single table, unrelatedto activity on other tables. Unless you want to log it alland not retrieve it, in which case why bother?

Paul: So you’d recommend that we don’t try it?

Andy: Unless you have a real overwhelming need, Iwouldn’t. In general, I think it’s of little value. It’s usuallysufficient to review the audit logs on a table-by-tablebasis, anyway.

• 01STONSC.ZIP—Source code for Robert Stone’s

article, “Using VFP to Visualize 3D Molecular Models.”

• 01EDITOR.ZIP—Accompanying demo versions for Whil

Hentzen’s editorial, “VFP Data on Your Palm Pilot.”

• 01DHENSC.ZIP—Source code for Doug Hennig’s article,

“Persistence Without Perspiration.”

• 0001KB.ZIP—Source code for Paul Maskens and

Andy Kramek’s article, “The Kit Box: Easy as Falling off a Log.”

January Subscriber Downloads

Paul: We always tend to think of logging in terms of usersmaking changes. One of the biggest benefits I see istesting our programs—particularly those thatprogrammatically change data in many records across thesystem. If we were go back to the multi-level commissionexample of a few issues ago (April 1999’s “The Kit Box:Buffering: The Transaction Slayer”), this would enable usto easily test the program by viewing the audit log forthe tables. ▲

0001KB.ZIP at www.pinpub.com/foxtalk

Andy Kramek is a longtime FoxPro developer, FoxPro MVP, independent

contractor, and occasional author based in Birmingham, England. He’s

currently working at Euphony Communications Ltd.

[email protected].

Paul Maskens is a VFP specialist and FoxPro MVP. He works as

development manager for Euphony Communications Ltd, and is based in

Oxford, England. [email protected].

Editorial: Palm Pilot . . .Continued from page 4

widths, as well as filter on one or multiple fields via afilter string using a “contains in,” “starts with,” “not in,”or “in range” operator.

The converter application that comes with JFileProruns on Windows 9x and NT, and it allows you to converta PDB (Palm database) file to CSV format and back. Thisfeature is pretty powerful, using an IFO definition filethat allows you to define and alter how data is movedbetween PDB and CSV formats.

JFilePro comes with two versions of its Help file—onein HTML (not .CHM) format, and an RTF document thatmakes good use of Word’s outline formatting so you cannavigate through it quickly.

Extended A rticles

• 01CORRE.HTM—“Visual FoxExpress 6.0” by Hector Correa.

A review of a tool that brings the n-tier architectural

movement to VFP.

• 01SCHUMM.HTM—“Deployment: Walkthroughs” by Richard

A. Schummer. Tips for the crucial walkthrough process of

application development.

• 01SCHUSC.ZIP—Source code to accompany Schummer’s

“Deployment: Walkthroughs.”

Page 24: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

24 http://www.pinpub.comFoxTalk January 2000

User name

Password

acrylic

bistro

The Subscriber Downloads portion of the FoxTalk Web site is available to paidsubscribers only. To access the files, go to www.pinpub.com/foxtalk, click on“Subscriber Downloads,” select the file(s) you want from this issue, and enter theuser name and password at right when prompted.

FoxTalk (ISSN 1042-6302) is published monthly (12 times per year)by Pinnacle Publishing, Inc., 1503 Johnson Ferry Road, Suite 100,Marietta, GA 30062. The subscription price of domesticsubscriptions is: 12 issues, $179; 24 issues, $259. POSTMASTER: Sendaddress changes to FoxTalk, PO Box 72255, Marietta, GA 30007-2255.

Copyright © 2000 by Pinnacle Publishing, Inc. All rights reserved. Nopart of this periodical may be used or reproduced in any fashionwhatsoever (except in the case of brief quotations embodied incritical articles and reviews) without the prior written consent ofPinnacle Publishing, Inc. Printed in the United States of America.

Brand and product names are trademarks or registered trademarksof their respective holders. Microsoft is a registered trademark ofMicrosoft Corporation. The Fox Head logo, FoxBASE+, FoxPro, andVisual FoxPro are registered trademarks of Microsoft Corporation.FoxTalk is an independent publication not affiliated with MicrosoftCorporation. Microsoft Corporation is not responsible in any way forthe editorial policy or other contents of the publication.

This publication is intended as a general guide. It covers a highlytechnical and complex subject and should not be used for makingdecisions concerning specific products or applications. This

publication is sold as is, without warranty of any kind, either expressor implied, respecting the contents of this publication, includingbut not limited to implied warranties for the publication,performance, quality, merchantability, or fitness for any particularpurpose. Pinnacle Publishing, Inc., shall not be liable to thepurchaser or any other person or entity with respect to any liability,loss, or damage caused or alleged to be caused directly or indirectlyby this publication. Articles published in FoxTalk reflect the views oftheir authors; they may or may not reflect the view of PinnaclePublishing, Inc. Inclusion of advertising inserts does not constitutean endorsement by Pinnacle Publishing, Inc. or FoxTalk.

Direct all editorial, advertising, or subscription-related questions to Pinnacle Publishing, Inc.:

1-800-788-1900 or 770-565-1763Fax: 770-565-8232

Pinnacle Publishing, Inc.PO Box 72255

Marietta, GA 30007-2255

E-mail: foxtalk@pinpub .com

Pinnacle Web Site: http://www .pinpub .com

FoxPro technical support:Call Microsoft at 425-635-7191 (Windows)

or 425-635-7192 (Macintosh)

Subscription r ates:United States: One year (12 issues): $179; two years (24 issues): $259

Canada:* One year: $194; two years: $289Other:* One year: $199; two years: $299

Single issue r ate: $17.50 ($20 in Canada; $22.50 outside North America)*

Editor Whil Hentzen; Publisher Robert Williford;

Vice President/General Manager Connie Austin;

Executive Editor Heidi Frost; Copy Editor Andrew McMillan

European ne wsletter orders :Tomalin Associates, Unit 22, The Bardfield Centre,

Braintree Road, Great Bardfield,Essex CM7 4SL, United Kingdom.

Phone: +44 1371 811299. Fax: +44 1371 811283.E-mail: [email protected].

Australian ne wsletter orders:Ashpoint Pty., Ltd., 9 Arthur Street,

Dover Heights, N.S.W. 2030, Australia.Phone: +61 2-9371-7399. Fax: +61 2-9371-0180.

E-mail: [email protected]: http://www.ashpoint.com.au

* Funds must be in U.S. currency.

FoxTalk Subscription Information:1-800-788-1900 or http://www.pinpub.com

Page 25: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 1FoxTalk Extended Article: January 2000

FoxTalkSolutions for Microsoft® FoxPro® and Visual FoxPro® Developers

This is an exclusive supplement forFoxTalk subscribers. For more

information about FoxTalk, call us at1-800-788-1900 or visit our Web

site at www.pinpub .com/foxtalk.

Extended Article

Visual FoxExpress 6.0Hector Correa

Visual FoxExpress 6.0 shapes the way you develop anapplication. Hector Correa offers this review of the productthat brings the n-tier architectural movement to you, the VFPdeveloper, in a clear and robust fashion.

“Language shapes the way we think and determineswhat we can think about.” —B.L. Whorf

VISUAL FoxExpress 6.0 was released in July 1999.This latest version of VFE was virtually redesignedfrom scratch. F1 Technologies has provided an

outstanding object model that’s both powerfuland flexible.

If you’ve read about n-tier application design and stillfeel that the idea doesn’t quite click with you, then take alook at Visual FoxExpress 6.0. Evaluating VFE is avaluable learning experience. The methodology andindications for implementing common design patternsbecome apparent just by reviewing the VFE object modeland its code.

Learning curveVisual FoxExpress is perhaps the VFP framework with thefastest learning curve. VFE 6.0 ships with an applicationmanager, several wizards, and builders that allow thedeveloper to create applications in a short time. You don’tneed to spend time memorizing class names, methods,and relationships, because the wizards and buildersautomate much of the process.

As with any tool, the first steps can be a little difficult.Follow the included tutorial step-by-step (I repeat, step-by-step). Deviating from the instructions the first timethrough might cause problems that transform the learningexperience into a nightmare.

Once finished with the tutorial, you can start creatingsmall applications and make changes easily. VFE wizardswill ensure that relationships between classes are setcorrectly. The application builder organizes classes by

layers (cursor, data environment, business, presentation,and form classes), clarifying the n-tier structureof applications.

After you’ve created a few small applications, youlikely will be creating classes using the wizards sparingly.By that time, you will have already figured out theclasses’ names, methods, and relationships.

DocumentationOverall, VFE’s documentation is good. The product comeswith an extensive Help file that introduces the frameworkand its components. This Help file also provides you witha list of considerations that you should follow while usingthe framework, and a reference guide for all classes,methods, and properties that are contained within it.There’s also a very comprehensive instructional tutorialthat demonstrates how to create your first n-tierapplication using local and remote views.

Unfortunately, there are no diagrams showing youhow the framework is organized. VFE’s structure isrelatively straightforward, but it seems that its creatorsfailed to take into account that some developers mightbenefit from viewing a diagram of the framework.

Another missing piece in the documentation is aprinted manual. You’ll need to print the Help file in orderto get written documentation. You’ll find that printingthe Help file (except for the reference guide) is a wisething to do.

Development methodologyVFE lets you create n-tier applications “out-of-the-box.”This allows you to produce applications that are alreadyseparated into logical tiers. Although the n-tier approachmight be new to some developers, VFE does a great job ofguiding you through the steps needed to create this typeof application.

First, the VFE application builder generates a VFPproject and a basic set of class libraries that you can use as

6.06.0

Page 26: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

2 http://www.pinpub.comFoxTalk Extended Article: January 2000

a starting point for adding your own classes. Then, theVFE application builder (see Figure 1) is organizedaccording to the logical steps that you should follow tocreate your own classes: cursor, data environment,business, presentation, and form classes.

From the application builder, you can use the wizardsto start creating your own application-specific classes and,thus, populate your project. The class wizard will createthe relationships between your classes as well. You canthen modify your classes to add code to their methods orchange their properties.

A common misunderstanding is that VisualFoxExpress is an application generator. This is an easymisconception for the casual observer, since VFE providessuch extensive RAD tools (including the applicationmanager and various wizards and builders). Thisimpression is completely wrong, however. VisualFoxExpress RAD tools create VFP projects and classdefinitions. They do not generate any code. They merelyassemble components and set their properties.Fortunately, using VFE RAD tools is not an all-or-nothingproposition—you’re free to use each of the RAD tools asmuch or as little as you need.

Development cycleThis is one area where VFE needs improvement,especially if you’re using the application builder. Theapplication builder is a great tool that lets you create VFEapplications with a minimum understanding of theframework and its structure. You’ll be using this tool quiteoften when you first begin to use VFE. However, becausethe application builder is modal, you aren’t permitted touse VFP tools, such as the command window, whileworking with this tool. The application builder also isvery slow when loading an application.

In addition to the application builder, there’s anotherweak point within the VFE development cycle: The formsthat you develop can’t be run in a stand-alone mode for

testing. You’ll need to launch the whole application inorder to test a form. This could be annoying if yourapplication has a long load time or a complicatedlogin process.

On the other hand, VFE does let you use thecommand window while you’re running yourapplication. You can suspend execution at any time anduse the command window or launch the debugger. You’llfind this feature of great value.

ArchitectureVisual FoxExpress’s architecture is perhaps its mostremarkable feature. It stands head-and-shoulders aboveother frameworks in terms of object orientation andn-tier design.

The VFE object model is extraordinarily well-done. Itprovides a robust and flexible class foundation for yourapplications. The framework includes presentation,business, and data classes. Each of them is laid out usingan n-tier approach.

Applications created with VFE are n-tier by default.Your presentation objects will talk to your businessobjects, and these will talk to your cursor objects. Theframework will automatically “wire” these relationshipsfor you. Communication between tiers is accomplishedvia message passing and clean component interfaces. VFEprovides all of the methods and properties required toallow the separate tiers to communicate, while respectingthe n-tier paradigm.

Figure 2 shows a simplified version of theframework’s n-tier structure. Due to the architecture that

Figure 1. VFE application builder. Figure 2. VFE architecture simplified.

Page 27: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 3FoxTalk Extended Article: January 2000

the framework provides, you can compile a VFEapplication as a COM object and then use its businessobjects through OLE Automation. This is a powerfulfeature that demonstrates the framework’soutstanding architecture.

In addition to the class structure that VFE provides toyour applications, the framework itself is organized intothree different types of libraries—base, intermediate, andapplication. Base libraries constitute the framework itself.The intermediate libraries are classes inherited directlyfrom the base libraries. By default, there’s no code in theseclasses. You can modify classes at the intermediate layerto change framework behavior without changing theframework base classes. Finally, the application librariescontain the classes that you’ll create to implement yourspecific application.

Another ingredient that complements VFE’sarchitecture is the data dictionary. A data dictionary is acentral repository where you can define the globalproperties of your database (field captions, default values,input masks, and validation rules) that will be usedwithin your application. VFE includes an active datadictionary that greatly reduces application developmenttime by using data-driven features that you typicallywould code by hand. The VFE data dictionary is so highlyintegrated with the framework that it allows you to definemetadata about classes and, thus, automate even more ofthe application development process.

The VFE Data Dictionary is based on DBCX version 2.DBCX is a set of public domain classes for the purpose ofmanaging metadata. F1 Technologies, Flash CreativeManagement, Micromega Systems, and StonefieldSystems developed DBCX jointly. The use of an openstandard allows VFE to integrate easily with otherpopular third-party products such as Stonefield DatabaseToolkit and FoxFire Reports.

FlexibilityVFE’s flexibility can be analyzed from two differentperspectives: the flexibility of the VFE framework and theresulting flexibility of VFE-based applications.

VFE, in general, is very flexible. As I mentioned, thethree different types of libraries that the framework usesallow you to organize your code very efficiently. You canstore application-specific code in the application classesand application-independent code in the intermediateclasses. In addition to the way the libraries are structured,the framework classes and their methods are very flexibleas well. You can use pre- and post-hooks methodsattached to most VFE operations to add additionalbehavior to the framework classes.

VFE applications are also flexible, since the n-tierarchitecture is, by definition, flexible. Given that yourcode will already be segmented into tiers (presentation,business, and data), your application will be very flexible.

For instance, you can change your data classes to point toa different data source (for example, a different DBMS),and your business and presentation classes will remainthe same. Likewise, you can compile your application as aCOM object and then reuse its business and data classesfor another front end (say, a Visual Basic application or aWeb browser application) using OLE Automation.

LongevityF1 Technologies has been working on release versions ofFoxExpress since 1991. The company released the firstversion of FoxExpress in 1992 (for FoxPro 2.0) and the firstversion of Visual FoxExpress (for Visual FoxPro 3.0) in1997. Visual FoxExpress 6.0 was released in July 1999, andit’s hard to think that an eight-year-old product isn’tfully mature.

VFE 6.0 was virtually redesigned from scratch,however, which unfortunately has led to a less than fullymature product. There’s a large amount of new code thatwill be put through its paces as time goes on. On theother hand, the redesign of the product allowed F1Technologies to come up with a very cleanimplementation of their product. They had time toremove (or modify) features that didn’t work well in thepast and replace them with new implementations.Furthermore, they took full advantage of the new featuresavailable in VFP 6.

Vendor/PrincipalsF1 Technologies has been in the market since 1990, and ithas a good reputation in the FoxPro arena. The two mainarchitects of the framework are Mike and Toni Feltman.They previously worked at Fox Software, the originalcreators of FoxPro. Mike and Toni are well-known amongthe VFP community, and they speak regularly at FoxProconferences in North America and Europe. Mike has alsowritten articles for FoxTalk and FoxPro Advisor.

PerformanceVFE applications have a reputation for difficulties withperformance. In fact, it can become an issue in yourapplications. As a result of its object-oriented design andits n-tier architecture, VFE performs a large number ofoperations in order to make an application work. Loadinga form can take a considerable amount of time, becausethe framework creates a large number of classes and bindsthem together on the fly. In addition, because theinformation is passed between three different tiers,loading a record and displaying it takes longer than itmight take in a standard VFP application. Therefore, ifperformance is a critical subject for your application, youshould be prepared to do extensive performance testingand tuning on your applications if you select VFE asyour framework.

F1 Technologies is aware of the performance issues

Page 28: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

4 http://www.pinpub.comFoxTalk Extended Article: January 2000

with VFE 6. They’re working very hard to improve thisarea of the framework. In fact, a new version of theframework will be released very soon (probably beforeyou read this article) that hopes to address some of theseperformance issues. This new version of the frameworkwon’t be a silver bullet, but it’ll certainly be a step in theright direction. As new versions of the framework arereleased and hardware keeps getting faster day by day,VFE applications’ performance might or might not be anissue in your applications. This, of course, will depend onyour particular requirements.

On the other hand, as I’ve mentioned before, VFE isso easy to use that you’ll definitely see a hugeperformance increase in your time to market for newapplications. In this area, performance achieved usingVFE is indeed hard to beat.

Source codeSource code is provided for both the framework and alldeveloper tools. Framework source code is included in thebase libraries, and VFE provides a catalog to access theselibraries from within the VFP component gallery.

VFE’s source code is very clean. Most likely this is aconsequence of a team with well-defined standards andgood coding practices. You might be surprised to findsuch consistency in such a large amount of code. Methodsare very short (just one page long in most cases) and well-documented. The documentation includes each method’spurpose, expected parameters, and return value. Thesefeatures are extremely valuable when you start steppingthrough the framework’s code.

F1 Technologies applied a large number of designpatterns (such as Controller, Abstract Factory) to theframework. It’s very interesting and educational to seehow these patterns were implemented using VFP. Youalso will find many implementation patterns as well(Delayed Instantiation and Composite Classes, forexample). Learning to implement these techniques andothers should prove mighty useful for your own code.

Developer toolsVFE provides a great number of tools to help you createcomplex applications quickly and easily. VFE includesboth visual and non-visual tools. The most notable visualtools are the application manager, wizards, and builders.You can use them to automate tasks during developmentto improve your productivity. Furthermore, becausesource code is provided, you can customize the nativecode to meet your own requirements. You aren’t tied tothe way they currently work.

You can use VFE wizards to create VFP projects, n-tierclasses, security components, reports, exportable data,switchboard forms, and toolbars, for example. Likewise,with VFE’s builders, you can set commonly usedproperties of various classes such as n-tier classes,

business object loader, and VFE classes that map to VFPnative controls.

There’s also a visual tool to handle the datadictionary. This tool allows you to navigate through themetadata and define its properties very easily. VFE’smetadata explorer is not as powerful as its Stonefieldcounterpart, although, since they both use the samefoundation (DBCX), you can fairly easily integrate SDTinto your VFE projects.

As part of the non-visual tools, you’ll find a largenumber of reusable classes that can enhance yourapplications. Some examples of these tools are theCollection class, Push/Pop settings classes, object-oriented Menus, Iterate class, Factory class, Registryclass, System Setting class, and Data DictionaryManagement classes, to name a few.

Data accessCurrently, VFE supports data access to VFP tables, localviews, remote views, and SQL pass-through. VFEsuggests the use of local views for all types of local data.If you follow this methodology, switching to remotedata is just a matter of creating a new database containerwith a new set of remote views.

VFE’s cursor class knows how to talk to a VFPcursor. Now, keep in mind that VFE has been designedas an n-tier framework. You can subclass the VFE cursorclass and make it talk to a completely different datasource (an ADO record set or an XML string, forinstance) without changing your business orpresentation classes.

Future versions of VFE will include support forthese and other non-VFP data sources. The foundation isalready there. You can implement it right away or waituntil it’s available from F1 Technologies.

SupportF1 Technologies provides support for VFE via telephone,fax, and their Web-based conference system. All newproduct purchases include 60 minutes of free telephoneor fax support. After that time has been used, you’llneed to pay for support based on their current rates.

Support on the Web-based conference system(http://www.f1tech.com) is free and unlimited. F1Technologies does a terrific job monitoring this Website. You can post your questions and get answersfrom the framework authors and/or other users ofthe framework.

In addition to their Web-based conference system,there’s also a VFE section in the Visual FoxPro Wikiforum (http://fox.wikis.com) where you can find FAQs,tips and tricks, source code, and a whole bunch of neat,shared information. Look for “VisualFoxExpress” and“VFE” entries.

Page 29: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 5FoxTalk Extended Article: January 2000

TrainingF1 Technologies offers a course on “BuildingApplications with Visual FoxExpress.” This is a four-day, hands-on course where you’ll actually build a VFEapplication. At the time of writing this article, the costof the course is $1,295 if you bring your own laptop(otherwise, add $300).

F1 Technologies also hosts an annual VisualFoxExpress Developers Conference, where the companyoffers short training courses and conference sessionsrelated to both the framework and VFP in general. Inaddition, F1 Technologies’ developers frequently speakat Visual FoxPro User Groups around the country. Youcan check their Web site for additional information.

Cost

The current price of Visual FoxExpress 6.0 is $595.Updates vary from $299 to $399. These prices are perdeveloper, and they all include source code. There are noruntime royalties.

To find more information about Visual FoxExpress 6.0you can contact F1 Technologies at:

335 North Superior StreetToledo, OH 43604-1427Phone: 419-255-6366E-mail: [email protected] site: http://www.f1tech.com. ▲

Hector Correa is a senior developer with Vision Data Solutions in

Independence, MO. He has been working with xBase languages since

1990. [email protected].

Page 30: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 1FoxTalk Extended Article: January 2000

FoxTalkSolutions for Microsoft® FoxPro® and Visual FoxPro® Developers

This is an exclusive supplement forFoxTalk subscribers. For more

information about FoxTalk, call us at1-800-788-1900 or visit our Web

site at www.pinpub .com/foxtalk.

Extended Article

Deployment: WalkthroughsRichard A. Schummer

How ready is the application? As a developer crunches outthe code to meet every whim of the customer, how much careis placed in crafting the code? This month, Richard Scummmertackles a process that’s very close to his heart—thewalkthrough process. It can be an exercise in showing off yourcode, or it can be an exercise in learning how to improve yourdevelopment style.

YOUR application is done. The customer calls havestopped, and the beta code is ready to roll out thedoor. You’ve worked all day and are ready to go

home when the director of development pops into yourworkspace and asks you for a status on the project. Youlean back in the chair with a wide smile and proclaim thatthe application is finished! All of the features have beenpolished, and the quality assurance team has pushed allof the buttons and signed off that the application is bug-free. Now you want to know when you can expect thatbonus check for beating the customer deadline and startplanning on what to do with the new purchasing poweryou just earned. Then the bubble is burst when thedirector of development asks, “When did you have thewalkthroughs scheduled? I don’t recall seeing them.”

Does this question bring panic, or are you ready toshow off the masterpiece that you finished assembling?Why the heck is a walkthrough considered a part ofdeployment process when it has nothing to do with thecustomer? Hopefully these questions and others you haveabout walkthroughs will be addressed in this installmentof the “Deployment” series.

Walking through a mine fieldWhil Hentzen refers to the walkthrough process as“Defending Your Life” in his book, The 1999 Developer’sGuide. My co-worker Steve Sawyer says showing peopleyour code is like showing people your underwear. Theseguys have hit the nail on the head. It’s a personal,nervous, and beneficial experience. You quickly realize

the impact of showing others your development, andyour code will be better as a result. Another termcommonly used to describe this process is a code review. Idon’t like to call them code reviews, since there are somany deliverables in the life cycle of a project that can bereviewed and have no code. In a certain sense, awalkthrough is like a trial. Unlike the American judicialsystem, however, the developer is guilty until proveninnocent by the reviewers.

The basic definition of a walkthrough is simple. Youhave other developers step through your code looking forerrors, bugs, performance issues, standards compliance,and support issues (a more complete list is available in the“Reviewer’s responsibilities” section of the article). Theytell you what they feel is right and wrong, providing onlyadmiration and constructive criticism. All criticism mustbe designed to improve the code once the suggestionspresented are implemented. The review needs to have atleast three participants, but in my experience, four isoptimal. Having more people can consume too much timeand starts to degrade the value of the review. This team iscomprised of the original developer and three reviewers.The reason I like three reviewers is that you’ll alwayshave a majority opinion when there’s an issue that’sdiscovered and not agreed upon unanimously assomething that needs to be addressed immediately.

The walkthrough timing is easy to determine. It mustbe done after the code is completed and before it’sreleased to production. This can be completed duringdevelopment, after unit testing, during system testing,user acceptance testing, and even beta testing. The laterit’s done, the less time there is to correct the issuesrevealed. I recommend walkthroughs be completed afterunit testing. Changes made to the code might not becorrectly implemented and can break the code that mighthave worked previously.

One argument I’ve heard over the years is thatwalkthroughs delay getting the product to market. This is

6.06.0

Page 31: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

2 http://www.pinpub.comFoxTalk Extended Article: January 2000

true in a sense, but in reality it saves time in the overallproduct lifecycle. Walkthroughs promote better code,which should translate into less production support time.Developers will learn better techniques from each other,which makes them better and likely faster. Anotherargument I’ve listened to is that a walkthrough isn’tbillable time to the client. I also disagree with thisstatement, but it’s up to the individual development shopto make this call. I consider the walkthrough process to bean integral part of development, as important asdeveloping and testing the application. Withoutdevelopment or testing, is the application released? No.The same goes for the walkthrough process.

Benefits include the following:• Readable code• Requirements met• Syntax bugs squashed• Standards enforced (and possibly enhanced)• Performance addressed• Train development techniques• Cross-training support team

So what happens if you’re a one-person shop? Thereare three possibilities that I’ve seen. The first is to partnerwith another developer, meet for breakfast occasionally,and swap code for review. Each of you will benefit. Thesecond idea is to find the local developer user group anddemonstrate pieces of the application, showing off thecode. Ask for suggestions and see whether anyone pointsout any problems they see. The last possibility, althoughnot as effective, is to review your own code. I’ve done thisand can say that it’s difficult at best. If you’re going toreview your own, get away from the code for a few daysor a few weeks before looking it over so that it has time tofade from short-term memory.

Alex, I’ll take Walkthroughs for $100There are several different categories of walkthroughs.Each type determines the type of people to invite and thetiming of when they need to happen.

Designs/data modelsI know this might be hard to believe, but you can actuallywalkthrough deliverables like system designs,requirement documentation, functional specifications,and data models. These types of deliverables require yourmore experienced staff. They might even includecustomers, depending on the topic and the sophisticationof the customers. Naturally, these walkthroughs happenearly in the development cycle, even when you’re goingthrough multiple iterations of the design.

One-time program/toolsOne-time programs might include a quick-and-dirtyconversion routine or queries to perform ad hoc reports(that always seem to get to production). Developer tools

are usually hacked together quickly to solve someproblem or remove some frustration in the daily tasks ofdevelopment. These rarely need a walkthrough, sincethey’re not used by end users. I like to have otherdevelopers review some of this code just in case Iforget something important. Typically, I have onedeveloper quickly check the code and give me theresults immediately.

Bug fixBug fixes are a common practice in our business. They canrange from a simple fix like a syntax error to acomplicated logic bug that might lead to a redesign of thefeature. The complexity of the fix dictates how manypeople are involved in the walkthrough. Simple fixes arehandled much like a one-time program; complicatedchanges get the works. Walkthrough timing is important,since bug fixes are usually time-critical. The two-business-day rule for review is often waived so the fixes get to thecustomer quickly. I like to invite some of the rookiedevelopers to learn from the mistakes made in the code inthese cases.

Application codeNewly developed features in an application get theworks. Enhanced features in an application get the worksas well. Is there a difference? In my opinion, the onlydifference is in the amount of code that’s reviewed. If thefeature is enhanced, I only expect the team to spend timeon the actual code that’s been enhanced. This might be anew method or a new object on a form. Don’t wasteprecious developer resources walking through code that’salready been reviewed. I use developers of all levels forthese reviews, and they typically happen as soon as thecode is unit tested.

Framework/library codeI know that all of the code is important, but theframework code needs extra special attention because itaffects every single application that it’s based upon.There’s nothing worse than implementing a change to thereport object, putting in a bug, and leaving for the day. Itnever fails—another developer will be putting the finaltouches on his release after you leave and every singleone of the 200 reports in his application will be broken.Trust me, it happens. Walkthroughs of this magnituderequire a special level of developer for the majority of thereview team. I still like to involve one junior programmerso they get some experience looking at code that’s “blackboxed.” This code needs a finer-toothed comb, and thisrequires more time than the two-business-day rule.

Preparing for the defenseThe developer needs to give the reviewers something to

Page 32: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 3FoxTalk Extended Article: January 2000

review; otherwise, there’s no point to this exercise. In myexperience, I’ve found that reviewing one feature of anapplication at a time works best—for instance, a dataentry form, a specific report, or a batch process. More thanone feature can complicate what the reviewers areevaluating, cause confusion of the requirements, or flatout just take too much time. The material created shouldtake no more than one hour to review and one hour todiscuss withina group.

It’s most important to have all of the code printed outfor the walkthrough. This way, the reviewers can look at itanywhere, anytime. I prefer to review code at home,relaxing on the couch, and sometimes while watchingtelevision. I want to be relaxed. I also treat is ashomework and do it at the same time the kids do theirs.All program code should be printed with line numbers sodevelopers have a reference when they get together todiscuss the issues found.

The packet needs to include every bit of code in thefeature being reviewed. This includes all code that’sdeveloped and called by the feature. Literally include anycode that will assist the other developers inunderstanding whether the feature developed meets therequirements. Here’s a list of things recommended for awalkthrough packet. Each walkthrough will likely have asubset of this list:

• Cover page (required)• Requirements document or section describing

feature (required)• Unit testing results• Screen shots/reports/label output• Program code• Form code• Class code• Report code• Label code• Query code• Text files• Table structures (with indexes, property settings)• And anything else that’s needed . . .

I can already hear a few developers screaming, “Howthe heck do I print out the code that’s stored in the VFPmetadata files?” See the “Tools” section later in the article,where I discuss some of the tools I developed (andincluded in the Subscriber Downloads atwww.pinpub.com/foxtalk) for this purpose.

In advance, the development staff will already befamiliar with the company coding standards andguidelines. If an individual isn’t familiar with them, printout a copy and introduce them to this importantdocumentation. They also need to understand theexpectations of the walkthrough process.

There’s no need to walk through third-party products

that are integrated into the application (although thismight be a fun exercise <g>). Also, framework code that’sused in the product shouldn’t need to be reviewed witheach individual feature. Naturally, there are exceptions tothis rule. One exception might be when the developmentstaff is changing the framework. This would apply to boththe enhancements to a third-party framework or anyenhancements to your privately developed framework.

The code packets need to be distributed to thereviewers at least two business days in advance of thewalkthrough. This gives the reviewers a chance to coverall of the material in a reasonable amount of time andallows them to make room in a busy schedule, allowingthem to meet their own deadlines.

Picking the review team is important. Eachwalkthrough might have special requirements. Things Iconsider when selecting the review team include subjectmatter expertise, subject matter inexperience (for trainingpurposes), support cross-training, walkthroughexperience, workloads, and schedules (like whetherthey’re going on vacation).

I assemble the material and then perform my ownwalkthrough. It gives me one more opportunity to catchissues before my team sees them. Once I’m convinced thatthe code is ready, I reassemble the packet, number thepages to help for reference during the review, and have itcopied and distributed to the review team.

Reviewer’s responsibilityThe reviewers are like a jury at a trial. They review thefacts presented and make a judgment in the end. Theyneed to make sure that the developer upholds thestandards and guidelines implemented at the company.They need to check that the requirements are met by thecode. Here’s a list of items that reviewers need to be sureto look for while reviewing the packet given to them:

• Feature is designed well• Code meets company standards• Code is readable• Code meets requirements• Test cases available and executed• Performance issues (optimized code, unnecessary

loops, and so forth)• Rushmore optimization (fast data access)• Appropriate comments, updated comments• Proper usage of development tool/language• Proper usage of framework• Proper scoping of memory variables• Repetitious code• Division by zero checks• Logic structures check other cases (ELSE,

OTHERWISE)• Return values• Appropriate data typing• No hard-coding of paths, filenames

Page 33: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

4 http://www.pinpub.comFoxTalk Extended Article: January 2000

• And so forth . . .

Each walkthrough will reveal more and more itemsthat you’ll check and add to the list. It’s important to givethe review your fullest attention and dedication to findingissues. If there are problems found, give suggestions andpossible workarounds. Note articles of your favoriteperiodical issue, and point them to other resources likethe Help file or online forums.

Included with this month’s Subscriber Downloads isCODEBAD.PRG, a program that I used to train thedevelopers at Kirtland Associates how to perform awalkthrough. It was intentionally written poorly to pointout some of the things I like to verify when I review code.

If you’ve opened it up, was that some painfulreading? It’s important to point out that source code is acommunication mechanism between developers. If thecode isn’t readable—if it can’t be understood—it’s notlikely that it will be easily supported.

Walking the plankSo judgment day comes; are you having a MaaloxMoment? One thing I constantly need to reminddevelopers is that we don’t get together to destroy eachother. Well, not the first time walking through the feature.If the code is bad and requires a re-walkthrough, andthere are major problems the second time around, well,then it’s fair game for a tar and feathering <g>.

Start the review on time. This is good advice for anymeeting. There aren’t many ways to waste time that areworse than sitting around waiting for someone to showup late. The developer conducts the meeting. I generallyask whether there are any general issues or questionsconcerning the requirements of the feature. After that, Iopen it up to the reviewers. I ask for the first page thatsomeone has an issue with. I hope no one speaks at thismoment, but that never happens. This is why it’simportant to print out code with line numbers and pagenumbers in the packet. The reviewers can reference theselines at this time. Start from the first one, move forwardto the next one, and continue thorough the packet untilthe end.

As issues are discussed determine which categoriesthey fall in:

• Must be fixed• Optionally fixed• Noted for future standards discussion• Noted for training topic

These categories aren’t mutually exclusive. I also referto the majority-wins rule as well. If one developer thinksthat I should add 300 lines of comments to describe a USEcommand and the other developers disagree, then I won’tmake a change. I might note it as a training topic for thatdeveloper in the future, though <g>. On the other hand, if

I’ve violated the standard that all commands should be inlowercase, and everyone agrees that I should be tied to anant hill with jelly smeared on me, I’ll have it fixed before Ileave for the day.

I find that these review sessions are an excellent wayto introduce the development staff to new techniques,new commands, and new design philosophies and takethe time to train them with discussion and heavy use of awhiteboard. As a reviewer, I’ve often asked the question,“Does this work?” Sometimes I do this because I know itdoesn’t work. Other times, I do it just to get somediscussion going on the issue I ask about.

SentencingOnce the code has been covered, the time comes todetermine the outcome of the “trial.” There are severalconclusions that can be selected from:

• Endorsed: All things considered, the developer escapesalive with at most minor fixes.

• Desk check: Issues were discovered and need to haveat least one person review fixes.

• Re-walkthrough: Developer loses a pint of blood andhas to go through the process again because majorissues were revealed.

• Management resolution: Developer continues to violategood coding practices and has to have correctiveaction taken by the boss. Tarring and feathering isn’tout of the question.

• Incomplete: Walkthrough was interrupted by a naturaldisaster and nobody was able to reschedule it.

• Cancelled: One of the developers sees major issues andcancels the review, so the developer avoids losing apint of blood or the painful removal of stickyblackened feathers.

• Other: Any other category not covered by thepreceding ones.

The conclusion is marked on the coversheet andsigned off on by the reviewer. The project manager retainsthese documents for future reference. As the director ofdevelopment, I like to note some of the issues found tohelp plan needed training for the development staff. I’veonly seen one management resolution walkthrough andonly a handful of re-walkthrough sentences in the past 10years. This comes from enforcing the “Defending YourLife” mentality. It usually takes only one difficultexperience for developers to pick up on what’s expectedfor these review sessions.

Mopping up the messIf issues are discovered during the walkthrough, thedeveloper is absolutely responsible for making thecorrections. The code will be better, and the system islikely to have reduced support calls (have I stressed thispoint enough yet? <g>). The enforcement process is

Page 34: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 5FoxTalk Extended Article: January 2000

important; otherwise, what’s the point of getting the codereviewed? Also included with this month’s SubscriberDownloads is CODEGOOD.PRG, an attempt to cleanup the code I used to train my team on thewalkthrough process.

I’ve also included a file called BADFINDS.TXT, whichlists some of the issues I found with the code.

Desk check, pleaseAnother alternative to a full walkthrough is a shorterprocess called the desk check. The desk check is a reviewthat takes place just like a walkthrough, but the review/walkthrough meeting is skipped, and the code reviewedis commented/noted on the documents and returned tothe original developer for changes. Fewer reviewers arealso included in a desk check—typically only one or twoare needed. A desk check is needed for quick bug fixesand small changes. It’s less formal and is quicker,but it’s still important and has the same impact as aregular walkthrough.

As noted in the “Sentencing” section, desk checks arealso used after a regular walkthrough if enough changes/issues are found and it needs to be verified that they wereimplemented as recommended.

Tools developedI’ve personally crafted a number of developer tools thatassist in printing out code that’s not readily availablefrom Visual FoxPro. These tools were originallydeveloped out of frustration, like most developer tools.They all print out code that’s stored in the variousmetadata tables. I also recommend looking at the codeinvolved in these tools. One item to note is the #DEFINEs,which point to the report that’s run for the output. This iscoded to my environment directories, so you’ll need tochange this to work in your development environment.

Form and Visual Classes (formrvw.zip)The Form Code Lister provides a printout of the detailsinside the Form and Visual Class Libraries files. First,you’re prompted for an SCX (form metadata table) or aVCX (visual class metadata table). Next, the programevaluates the different objects. The listing presents thecode (methods) and attributes (properties) for each object,as well as some other details stored inside the metadata.The objects are grouped by container (that is, all objects ina Grid, all the objects on a Page inside a PageFrame, andso forth). The output is presented in a report preview sothe developer can check it out before printing the reportto the printer. Please note that even average forms cangenerate 30 pages of output. Great care was taken to keepthe output as small as possible. An alternative to this is touse the VFP Class Browser, which outputs some of thesame information in object order.

Reports (rptrvw.zip)The Report Code Lister provides a printout of the detailsinside the Report Form file. First, you’re prompted for anFRX (report metadata table). Next, the program evaluatesthe different objects. The listing presents the code(methods in the Data Environment or fields on the report)and attributes for each object, as well as some otherdetails stored inside the metadata. The objects are listed inthe order they appear on the report, from top to bottomand left to right. The output is presented in a reportpreview so the developer can check it out before printingthe report to the printer. There’s no tool in VFP that listscode stored in the report form.

Menus (menuwalk.zip)The Menu Code Lister provides a printout of the detailsinside the Menu metadata file. First, you’re prompted foran MNX (menu metadata table); the program evaluatesthe information in the metadata table and then lists offeach of the menu options. Information included is PadPrompts, Bar Prompts (along with relative position on thedropdown), Status Bar Messages, hot keys, procedurecode (setup, cleanup, and procedures for each bar),relative positioning of Menu Pads, and bar names. VFPstill generates .MPR code that can be reviewed as well. Iuse both the program code and my tool, since my toolgives a visual presentation of the menu that anMPR doesn’t.

Structs.exe (structs.zip)I think most FoxPro developers have found the LISTSTRUCTURE command to be a bit lacking in usefulness. Ialso believe most developers have either cobbled togethertheir own alternative of LIST STRUCTURE or found aprebuilt one on the various Fox Forums. I’ve included thetable structure-documenting program developed byDavid Denning at Kirtland. I think it’s one of the finesttools of its kind and is extremely helpful for developmentand walkthroughs. Output can go to the printer, preview,or the clipboard. The clipboard is helpful if you want topaste the structure information to a technical document orfunctional specification.

WTCover.docThis isn’t a tool; rather, it’s a document that we use atKirtland Associates as a cover sheet to a walkthroughpacket. You might want a starting point to developyour own, so I thought I’d include it in theSubscriber Downloads.

Note that these tools are updated as needed or as theenhancement requests are processed. These will beavailable at my personal Web site at http://my.voyager.net/rschummer.

Page 35: FoxTalk - dFPUG-Portalportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0100.pdfJanuary 2000 Volume 12, Number 1 FoxTalk Continues on page 5 Solutions for Microsoft® FoxPro® and

6 http://www.pinpub.comFoxTalk Extended Article: January 2000

The next stepKeep showing off your code until next time. I think I’llfinally attempt to cover the building of the “gold” codethat will be shipped to the customers, and then buildingthe installation process. Feel free to send me yourwalkthrough ideas, and I’ll pass them along as theseries continues. ▲

01SCHUSC.ZIP at www.pinpub.com/foxtalk

Rick Schummer is the director of development for Kirtland Associates,

Inc., in Troy, MI, which writes custom database applications for an

expanding customer base. After hours, he enjoys writing developer tools

that improve his team’s productivity and occasionally pens articles for his

favorite Fox periodicals and user group newsletters. Rick is a founding

member and secretary of the Detroit Area Fox User Group (DAFUG) and

Sterling Heights Computer Club. He’s a regular presenter for these

organizations and other user groups, and he presented at Microsoft’s

DevDays. http://my.voyager.net/rschummer,

[email protected].