VFP doesn't have truly static variable as in
other languages. i.e. Variables that are local
to a function, but maintain their values between
method invocations.
Although a class instance will maintain the state
of its own variables, that's not the true defintion
and purpose of static class variables.
One way to accomplish true separation; where no other
function, procedure, or class can directly access the
static variables; is to create two types of objects:
1 - A class factory object that holds the static variables
2 - A concrete object where the actual work is done.
As many concrete objects as needed can be created via
a call to the factory object. Then it's just a matter
of making calls on the instanced concrete objects to
access the static variables.
The following example, although not very usefull will,
illustrate the concept.
[tt]
CLEAR
* Instance the factory and concrete objects
LOCAL oFactory, oConcrete1, oConcrete2, oConcrete3
LOCAL i, j, lcMacExpand
oFactory = CREATEOBJECT("C_Abstract_Factory"

oConcrete1 = oFactory.CREATE()
oConcrete2 = oFactory.CREATE()
oConcrete3 = oFactory.CREATE()
FOR i = 1 TO 2
FOR j = 1 TO 3
* Execute methods on concrete objects
lcMacExpand = "oConcrete"+STR(j,1)+".BumpVars()"
&lcMacExpand
lcMacExpand = "? oConcrete"+STR(j,1)+".GetVars()"
&lcMacExpand
NEXT
NEXT
oFactory.DESTROY()
* Classes
DEFINE CLASS C_Abstract_Factory AS CUSTOM
* Tracks concrete objects
hidden array aObjects[1]
hidden nObjects
DIME aObjects[1] && Array of created objects
nObjects = 0 && Count of created objects
* Static variables
* Unfortunately, their is no way I know of that will
* make these variables visible only to the concrete
* classes without using direct method calls
* (access/assign) on the factory object. The
* access/assign methods will insure that only the
* objects it instanced(the factory object) can modify or
* read the static properties. Of course using this
* methodolgy will incur a serious performance hit.
cVar1 = ""
cVar2 = ""
nVar1 = 0
nVar2 = 0
FUNCTION CREATE
THIS.nObjects = THIS.nObjects + 1
DIME THIS.aObjects[this.nObjects]
THIS.aObjects[this.nObjects] = CREATEOBJECT("C_Concrete_Object",THIS)
RETURN THIS.aObjects[this.nObjects]
ENDFUNC
PROCEDURE DESTROY
LOCAL i
FOR i = 1 TO THIS.nObjects
THIS.aObjects[ i ].DESTROY()
* Could be set to null, but an
* explicit destructor call avoids
* any possiblility of a VFP memory leak.
NEXT
ENDPROC
ENDDEFINE
* Although the concrete objects have their
* own state, the psuedo static variables
* contained in the factory object instance,
* are the ones that the methods of these
* concrete objects are concerned with.
DEFINE CLASS C_Concrete_Object AS CUSTOM
oSourceOfStaticVariables = NULL
PROCEDURE INIT(loFactoryObject)
THIS.oSourceOfStaticVariables = loFactoryObject
ENDPROC
PROCEDURE DESTROY
THIS.oSourceOfStaticVariables = NULL
ENDPROC
* Function and procedures
FUNCTION GetVars
LOCAL lcValuesOut
WITH THIS.oSourceOfStaticVariables
lcValuesOut = ;
.cVar1 + .cVar2 + ;
"nVar1 = "+TRANSFORM(.nVar1)+",nVar2 = "+TRANSFORM(.nVar2)+","
ENDWITH
RETURN left(lcValuesOut,len(lcValuesOut)-1) && Strip last ","
ENDFUNC
PROCEDURE BumpVars
WITH THIS.oSourceOfStaticVariables
.cVar1 = .cVar1 + "cVar1_Pass"+TRANSFORM(OCCURS(",",.cVar1)+1)+","
.cVar2 = .cVar2 + "cVar2_Pass"+TRANSFORM(OCCURS(",",.cVar2)+1)+","
.nVar1 = .nVar1 + 1
.nVar2 = .nVar2 + 1
ENDWITH
ENDPROC
ENDDEFINE
[/tt]
'We all must do the hard bits so when we get bit we know where to bite'
