Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Public Variable 3

Status
Not open for further replies.

Scott24x7

Programmer
Jul 12, 2001
2,826
JP
I see a lot of discussion and some very religious arguments about Public Variables, so I don't use them lightly, and when I find I'm about to I give it a lot of thought.
One instance in particular is, the status of showing tool tips. Some people like them, some people hate them, so I give the option to turn them on or off. This is done with a Public Variable called glTipState. It's a logical value, only .T. or .F.

So for the no public variable purist, what danger am I really facing here? And I'm also reminded that the machines we use today are massive boxes with memory bigger than hard drives were in the day VFP had it's last horizon even. So, why is this a problem? Or is it?


Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Hi Scott,

I never ever use public variables. First of all, I have a global application object which has a "variable container" class, in lack of a better word for it. In cases where a public variable would be much handier that using this class, I use properties of _screen, like _screen.cClientName.
 
Common, use properties of _SCREEN or _VFP or of one public variable and object goApp. There is no need to have a public variable per simple type of data just because you need to address it everywhere.

There is a bug about public variables, that alone aside of the theory speaks against using public variables in VFP (see They are and stay bad, no matter what new arguments you think you can add to the discussion. Any name collision can cause redefinition errors and worse things. A name collision in goApp property names, for example, can be avoided or made much less probable by having goApp.oSetup.propertyname, so even if some other developer adds in his property and names it like one of yours, he probably will still add it to the goApp level and not knowing goApp.oSetup. So you have that namespace for yourself. Namespaces are the main thought here, and you don't really have them in VFP.

You always can argue you are the only developer. That's a moot point.

Let's just for a second assume it would be okay to use them. There is the PUBLIC keyword in VFP, isn't there? Well, just thinking about the developer taking over your code, he has to find out where they are defined, any object/class has a class and classlibrary, so I can find out about the root code. Variables don't have that and thus are mainly for local scope, where one method/event/procedure/function gives the whole scope of it. That means alone the documentation point of view is bad about public variables.

The on/off setting you mention is a setting, this goes into setting/config data. You don't need a public variable to access it, you need a data access object of a settings table or file and may read the values in once only or cached, allowing changing the setting at runtime. This is no candidate for a public variable at all. Besides on this very specific topic, you can simply set form.ShowTips= .t. or .f. and that's it.

Bye, Olaf.
 
Hi Olaf, and Tore,
Well, that's interesting, I didn't know there was a bug with Public in VFP. In reality this public does carry over from my FPD2.6 days and it's just persisted. So I got to thinking about it, and why it might be so horrible bad to have around. I don't fully grasp the concepts of the solutions you've mentioned, but I'll try to work with it a bit more and understand it. Strangely the "visual" I get from your description is like a big ball of gum that just has everything stick to it that it touches. So that's a "mystery" to me. I'm starting to make more sense out of some of the things you mention though. I have already the concept applied as well, that has a table that goes with each installation of the application called "UserTable" which keeps settings like where the app was last open and positioned, size of window, it does hold the value of gltipstate so that if they set it off once, they don't have to turn it off every time. But that means you have to make a call to the table every time you enter an object to check and see if it's enabled or not, and that seems tedious, so perhaps another method as you've mentioned above is better. I've kind of ignored this one a long time because it seems rather innocuous. But I'll give it more thought, and see how I can integrate these ideas into my current app. I know it's second nature to you guys, but it feels very foreign to me.


Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
The way I handle this sort of thing is by having a single application object. That object has properties for all the things I want to be able to see anywhere in the application. (In reality, rather than having dozens of properties of this one object, I generally have other objects to handle specific parts of the application, and the application object has properties to point to those objects, but that's more complex than you need to know now.)

Early in my main program, I instantiate the application object and store it in a private variable (that, for historical reasons, is called goApp, even though it's private). That code (somewhat simplified) looks something like this:

Code:
PRIVATE goApp

goApp = NewObject("cusAppClass", "AppClass.PRG")
IF ISNULL(goApp)
   * Tell the user we can't start
   RETURN
ENDIF

From there on out, I can use goApp. What does the class definition look like? Again, more complex than I want to give you here, but for your very simple situation, something like:

Code:
DEFINE CLASS cusAppClass AS Custom

lTipState = .T. && or .F., if that's your default

PROCEDURE INIT

This.RetrieveSettings()

RETURN

PROCEDURE DESTROY

This.StoreSettings()

RETURN

PROCEDURE RetrieveSettings
* Code to look up your stored value for tooltips
* and anything else and store it into the
* corresponding property/properties.

* For example:
This.lTipState = UserTable.lTipState

RETURN

PROCEDURE StoreSettings
* Code to put any change settings back into 
* your storage table.

* For example:
REPLACE lTipState WITH This.lTipState IN UserTable

RETURN

ENDDEFINE

As I said, this is much simplified, but I hope will get you started.

Tamar
 
Scott said:
But that means you have to make a call to the table every time you enter an object to check and see if it's enabled or not, and that seems tedious
Well, your program code for this and call t. You may have this call in a base class. And then you're done with it forever. If you're concerned with the performance hit, I already mentioned caching. VFP does that without any ado from your side, but you may as I hinted add an oSetup object (eg Collection) to your goApp or to _SCREEN or whatever single public variable instance you want to use. As I said in regard of the top-level form, you know you can't get rid of _screen, so you can - and many VFP developers do - use it as the single public object becoming - as you put it - a big ball of gum. Kind of, but with defined spots for stuff, your name spaces, if you like. To add to _screen, well, you know _screen is just a form, it has AddProperty and AddObject methods, so you can add to it. There's no magic trick here, just making use of what's there anyway.

What speaks for a goApp public application object instance is that this can not just be your big ball of gum, but be an active component implementing global behaviour. You can think of it as the main application controller or business logic, too. A typical application class could be based on container to be able to add in modules, managers: A form manager, a database manager, a language manager.... all components, which care for one aspect and can be "asked", can be called for getting some data, including such settings.

And the advantage of not only reading some setting at program start into a public variable but read it from a dbf everytime is, the user can change that setting and let it have an effect on the next forms started without needing to restart the whole application so it reads in the new value.

Your whole application is concerned with data you most probably have in a LAN share, adding in one read of a few records coming from a local usertable won't hurt.

Bye, Olaf.

 
So here is a crazy question...
What about using a FormSet for this?
If I understand correctly, all the forms within the FormSet can "see" the FormSet, and properties of the FormSet are accessable to all the forms within by reference ThisFormSet.property. If you create properties as members of the FormSet, wouldn't this have a similar effect to "Public" without declaring any publics, and kind of be like the goApp object, but without the need to add the nonvisual class to every Form?

Am I understanding that reasonably?


Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
the goApp object everyone is referring to isn't added to every form.

You create it once in your main .prg at startup

most use it to actually perform the startup procedures

e.g.

a typical main.prg might look something like this

PARAMETERS tcDBC
PUBLIC goAPP
goApp = createobject("myapplication")
goApp.declaredlls()
goAPp.setEnvironment()
goApp.openData(tcDBC)
goApp.login()
goApp.setToolBar()
read events


then any values or settings you need anywhere/everywhere in the application are declared and initialised within the application object and your code just references goApp.whatever


Not used them myself but the general consensus is FormSets are best avoided.

hth

n
 
Nigel, but then you're declaring PUBLIC goAPP so that totally defeats the purpose.


Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
You hit another weak spot of VFP - form sets. It's recommended to not use them. Also, you would need a form set with all forms so your application would always start up all forms and only have one common datasession. Even if the form set itself would behave nicely, this is no good idea at all.

As Nigel says there is the strategy to have one public object goApp. It can be based on custom or container, doesn't really matter. As it's created first, it'll live in the general/default datasession ID 1, if that ever matters. I already laid out, how goApp can be composed to be the host of many managers and that's what's used. You limit the use of public variables to one global object, and what helps is you don't need to declare go APP as PUBLIC, you can simply create it in main.prg, it becomes a private variable and as main.prg stays on the program stack for the whole lifetime of an application that means it's a quasi-public variable. And that is fine, and you can permit it in any code addressing it by PRIVATE ALL EXCEPT goApp and have no side effects though other accidentally private variables.

Notice: In all my coding I never (very scarce and rarely) use private variables, they also rather pollute the namespace and only in clearly and heavy recursive functionalities can save the time of passing values in and out. Or said simpler: Besides goApp, any I declare any variable LOCAL, if a variable becomes public, then only by accidentally forgetting a variable declaration.

Notice: You don't need to declare a public just because you need a value at two places, not within the same class or PRG. What you really need is access to such a value, and that can be by code you call. It's totally normal to define functions usable from anywhere via SET PROCEDURE or classes instantiable from anywhere by making their location publicly known via SET CLASSLIB. These are your public helpers and friends, you don't even need a goApp, it does not mainly justify itself by being public, but it is public by providing functionalities of public interest.

Bye, Olaf.
 
Ok, I'm starting to get this. Sorry the concept is still eluding me a bit.
I have a problem I'm trying to solve, that I think may be related (may be the solution), but I will start a new thread, as it's a little different.
I'm going to implement this goAPP thing in the next day or so, but I want to run a couple of small tests first to get my head around it.
Tricky as I'm kind of tying 3 or 4 related things together now, so that adds some complexity to my understanding.
Thanks for being patient with me.

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Scott, in the 22 years that I have been using VFP, I've never used a formset and I'm not aware of anyone else ever using them. I'm not saying there is anything undesirable or nasty about them. But it's one of those features that were included for backward compatibility only and which seem to have no place in VFP.

(What's the betting that at least three of the forum regulars will now pop up and say that they use them all the time and could not manage without?)

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike, LOL well that's part of the beauty of it isn't it? Many solutions to one problem, and that's really a good thing.

I've started a new thread which fully describes the real problem I'm facing now. To be fair, I think it's one of the most complicated issues I've faced in VFP, and I feel it's pushing my capability upward which is really good. I've been working with C# lately as well, and I have to say the more I work with it, the more I really appreciate VFP... C# has made me a better VFP developer, but I don't see moving to it after all, as I had originally planned. "Let's take what we used to do in <whatever time> and make it take 4 times longer!" And that's not just a lack of understanding it, it's just so tedious for no valid reason (my perspective).

So the main reason I looked at FormSet was one of the implementations of RTF control I had used back 15+ years ago, used a FormSet to make the RTF available across pages in a pageframe. But I quickly discovered, I've a much bigger problem in trying to get them to work "outside" a form, which is the subject of the new thread.


Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Just want to add my opinion that formsets are never to be used. They were added as a way of converting FP2.x's screen sets. But it turned out that converting FP2.x stuff was a bad idea, so they weren't used much. They're kludgy and under-tested.

As the others said and my code shows, goApp is private, not public. But even it if were public, it would be just one variable with a known name, as opposed to having dozens or hundreds of them (as many applications I inherit have).

Tamar

 
In my goApp object I have this little function:

[pre] Function This_Access(tcMember)
If ! Pemstatus(This, tcMember, 5)
This.AddProperty(tcMember, Null)
Endif
Return This
Endfunc[/pre]

This makes it possible for me to write for instance "goApp.cMyText = 'This is only a sample'", which will add cMyText as a property if it doesn't exist, and assign the text to it.
 
Tamar,
It's funny because as I look back at that, I was at DevCon in 2005 in Phoenix, with the launch of VFP3 where MS gave everyone a copy who attended. (I remember attending your session on user interface design, and if I remember correctly the segment on "Hostile User Interface" was very amusing. But the point here is, I also remember attending a session where MS was talking up this great "FormSet" idea, and so it kind of stuck with me. I remember a few years later converting my old FPD and FPW 2.6 apps to VFP 6, around 2002. But I did those by opening them side-by-side, taking the logic out of a FP app and rebuilding it in VFP using better native language. (I also remember finding that tons of things we'd had to write before were suddenly native function calls, so threw away tons of code then too).
I'm finding having attend DevCon in 95 and 96 was probably a bad idea in some ways, because there are a lot of things I recall from then that were supposed to be "The way to do things" that now are "Never do that". FormSets, Data Environment, are two off the top of my head. I know there are more...

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Hi All,
So I got goAPP implemented. A little tricky, but I understand what it's doing. Pretty clever. So far just the simple global logical glTipState, as I mentioned, but it cracks the door open to a lot more. Thanks for helping me through this. It solves a lot of problems!

Tore,
I'm about to add that function you listed too. One question I have, what is the expectation of the prefix in your variable you use "t" (as in tcMember)? I know it doesn't impact the function, but I having seen a t prefix before. Also, I see the crazy usefulness of this function! And I can assign it anywhere. So that is going to simplify a ton of other logicals and values I've been "going to tables for" now I can do it at init, and then just reference the goAPP.<value>. So things like userid, username, accesslevels can all be assigned with goAPP.glUserName = USERTABLE.USERNAME and now, I have a glUserName across the application. Brilliant!
Particularly where I don't need to write the result back. Quite clever!

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Hi Tore,
So I put that code into my DEFINE APPCLASS.PRG, but when I try to call it as you describe with something like:

goAPP.TesTore = "Check This OUT"

I get an error saying "Property TesTore is not found".

Should the function code be somewhere else?

Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Ah, got it working.
STUPID.

I accidentally put it at the very end of the AppClass.PRG, AFTER the ENDDEFINE clause. Moving it up on line solved everything.
Now... I can't wait to exploit it. :)
Thanks Tore!


Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top