Are Global Variables Really that bad ?

I make no claim that what I do is "right." What I do is the way I like it.
Mark

In the end that's all that matters ... as long as it works reliably for the developers and the end users.

FWIW here's part of the modDefinitions standard module containing the kind of global variable I do use in one of my databases ...

attachment.php


Again its just what I do (for items referenced repeatedly by the program)
... but less so than in the past
 

Attachments

  • Capture.PNG
    Capture.PNG
    36.8 KB · Views: 895
...I almost always, for instance, write a cUser class that has rich data--like date/time of login, machine name, security info, prefs--about the current user. This class would be created automatically using WScript.Network.Username and drawing other data from a table.

When I first became aware of the existence of Class Modules, I struggled with finding a practical uses for them. The cUser Class you describe is one of the situations I thought of.

While is was trying to figure out if the juice was worth the squeeze, I stumbled across Type variables instead and went that route. I would like to say I chose that route for efficiency reasons but truth be told, it was because it was MUCH easier to figure out!

All that being said, in this particular case, does one course have any advantages over the other? Specifically data stability (scope) and system resources are the two main advantages that come to mind.
 
does one course [Type or Class] have any advantages over the other? Specifically data stability (scope) and system resources are the two main advantages that come to mind.

A Type cannot support any validation of its component values beyond the datatype. A Class supports Properties with Get and Let procedures that can do pretty much anything.

A Class can have Methods.
 
Apologies Galaxiom, I am not a programmer. By “methods” I assume you mean Subs and Functions?

Class Modules have intrigued me from the moment I first read about them. I have invested some time in trying to learn how they work and how to apply them, and the quest continues - in regards to learning how they work. My brain simply isn’t wired right when it comes to OOP.
 
Gent, a Class has properties and methods, not functions or subs or global variables, when it is well defined. Here is the rub:

Inside the module that defines the class, those things are private functions or subs and their entry points are defined using the normal Function and Sub declarations. However, from the outside of the class, you invoke them differently. The variables in the class module's declaration area are declared normally, but you then have to arrange for Property Get and Property Set "functions" to involve the hidden elements that the class REALLY uses when it does something.

So then, if set up correctly, the class-obj.XYZ = 3 method ACTUALLY DOES call a subroutine that sets the private variable XYZ to the indicated value. The class-obj.Move(X,Y) really DOES call a subroutine to move coordinates in some way.

But the SYNTAX by which you use it suddenly makes the class object behave like a form or control or whatever, with the same VBA syntax as any other encapsulated object. And this is done because of course, if you have more than one of these class items, using a global runs afoul of over-defined global names - including global Function or Sub names. This multiplicity of definition pretty much guarantees massive headaches.

But if you have multiple objects of that class, each object has its own private copy of the properties or other content, and is qualified by the name of the class object you created as a member of the class. Which is EXACTLY why you can talk about FormA.Recordsource and FormB.Recordsource and in each case, know exactly which recordsource you were discussing.

This is one ASPECT of enclosure and encapsulation. There's a LOT more to it than that, but it should be enough to explain what you need to know to get past the confusion.
 
Sorry Doc, there are numerous errors in your post. I am sure you know better and just posted without reviewing what you must have typed at phenomenal speed. You should try again.
 
G, it has been years since I did one of these and it wasn't implemented in VBA.

Gent, take my previous description with a grain of salt because I DO respect Galaxiom and don't care to get into a big dispute over something that to me was an explanation of WHY more than HOW. Even if I'm wrong technically for HOW it is done with VBA, I am right conceptually about WHY you do it.

When building a class object definition, you convert private subroutine calls (defined inside the class module) into Methods of the object and you convert simple Property assignment operations into functional calls (also inside the module) that affect the actual variable declarations inside the object. The details of how that is done do not matter for the discussion at hand.

You do it so that if you have two objects of the same class, you use the names of the objects to differentiate between the variables declared inside the class definition for each object. And you have two because when you instantiated the two objects, you instantiated copies of any required data structures associated with those objects.

So, for example, there probably IS some sort of internal variable within Access's definition of a Form object that is called "Width" (or, who knows, internally it would be called FormWidth... a rose by any other name). You cannot see that actual variable because the class object has code that intervenes when you attempt to set or get the value associated with that property. That intervention can filter stuff, raise errors, or actually do what you asked it to do. Since the object is a "black box" you don't really know - but you know it has to be there in SOME form.

Oh, by the way... since all variables internal to a properly constructed class declaration are Private, we adroitly side-step the "Global Variables" issue by using class objects.
 
No worries folks, I was a little concerened about hijacking the thread but Doc brought it back on point.

To offer a less than informed perspective, I have never liked the idea of a Global unless of course it actually IS a constant. MarkK’s example of Pi is a good one, although I am sure The is already a built in function for it.

In my case, I have never needed them and I liked the idea that they went out of scope once the Module that used them terminated.
 
Gent, take my previous description with a grain of salt because I DO respect Galaxiom and don't care to get into a big dispute over something that to me was an explanation of WHY more than HOW. Even if I'm wrong technically for HOW it is done with VBA, I am right conceptually about WHY you do it.

Yes. Doc was conceptually more or less correct but it wasn't expressed so well and the details were messed up a bit.

"Methods" are generally Public Subs. (That is just what are called from the outside of the object.)

A Public Function can also behave as a Method but then it has to use function syntax (parentheses around the parameter group etc) to get the return value. It can get very confusing especially if there is a single parameter because parentheses around a parameter alters how it behaves. Best stick to using Subs as Methods.

Normally one would use a Property to record or return a value from a class object. A Property value is usually stored as a Private variable preventing access to it other than via the Property. The Public Property GET and LET are used to read and write to and from the Private variable which provides tight control over what will be accepted. The Variable must have a different name from the Property.

The main purpose of a Class object is to support multiple instances.
 
Just for clarity, G, in the system where I implemented the object, the sub wasn't declared as Public because this particular language had a different keyword for this case. It has been so long, I don't remember ALL of the details. Nor does it matter.

As to "not expressed so well" - I must admit the post to which you objected was composed near midnight local time. The fingers still work OK but the brain begins to shut down by then.
 
The biggest issue with extending the scope of variables is that you need to keep careful track of their use. As long as you are careful, there are no issues. However, if you use functions, with passed arguments, and define variables within the functions, then there is much less chance/likelihood of such errors.

By the same token, I prefer to use byval arguments, rather than byref, to avoid the possibility of inadvertently changing the variables.

With all that in mind, using public variables sensibly can make development much easier in some cases.
 
The biggest issue with extending the scope of variables is that you need to keep careful track of their use. As long as you are careful, there are no issues. However, if you use functions, with passed arguments, and define variables within the functions, then there is much less chance/likelihood of such errors.

By the same token, I prefer to use byval arguments, rather than byref, to avoid the possibility of inadvertently changing the variables.

With all that in mind, using public variables sensibly can make development much easier in some cases.

There is nothing you have said with which I disagree.

Best,
Jiri
 
I see this is still going on. I'll try to avoid beating a dead horse here. (According to the Woody Allen movie What's Up, Tiger Lily, that would make me a sadistic sodomistic necrophile... but I digress.)

Jiri was emphatic in expressing feelings over the idea that globals have their place. Galaxiom tends to avoid globals. Plog and Ridders have their positions. I am a known pragmatist and have guidelines about when I use them and when I don't. However, something said in passing needs addressing.

When you are dealing with a team of programmers, each responsible for constructing parts of a project, you CATEGORICALLY NEVER allow use of a global for any part of any programmer's piece. ALL repeat ALL assignments to "code up this function" or "write this subroutine" will include the strictest possible admonition to NEVER depend on a global - with one exception.

That exception is that the team's leader lays out what will be allowed as a global variable and NOBODY ELSE will create one. EVER.

The key here is coordination among people who have high intelligence and vivid imaginations. When one person is the entire development team, coordination with regard to globals is reasonably easy. When you actually DO have a team, you will NOT achieve coordination unless you lay down hard law on which globals will and will not be allowed.

I've done that more than once on teams I have led (in other languages). The moment you allow someone to say "I need this to be global" then you have opened up the path to unbridled global scope - and in so doing, you lose the ability to re-use your modules in other projects. As a software engineer, you cannot afford to write non-reusable code. Your most expensive commodity is your time and to waste it by making too many one-off projects due to modules that defy reuse is to cheat your own wallet.

This has been my experience as a project / team leader. My project experience over a period of over forty years is why I am very careful about globals. Not to the point of exclusion. But very careful of them in ANY language.
 
@MarkK, what do you do when you have data that needs to be shared by several procedures?
I really don't see this happen very much. Almost all code I write is in a class, so the class itself either knows how to get at the data it needs, or it exposes other classes which instantiate automatically, and which also do their own data access. As such, I rarely find myself passing data around to disparate procedures. To answer this more specifically, can you describe a case? Then I can say how I would handle that case.

I typically expose a few global classes that are costly to construct, like you, from a hidden form. System settings, for instance, I commonly expose as a global class with named properties so its members are all available via intellisense, which simplifies other code. I'll also commonly create and expose a global cUser class which in a bigger system might itself expose a cUserPreferences class, similar to the cSysSettings, with named properties available via intellisense.

Also, keeping track of classes and their exposed properties at runtime is super easy using the IDE's locals window, which evaluates Property Get procedures automatically, and shows child classes as expandable nodes in a tree view.

Otherwise I never--and I mean never--use standard module system-wide globals to transfer simple data types between data producers and data consumers as an intermediate step in a coded process. And it's not that I think others shouldn't, but to me it just always works out better if that would-be global is a member of a class.

Mark
 
I read Pat's viewpoint and I agree with most of it. Using a form as a variable-keeper is an interesting debugging trick that never occurred to me. Thanks for that one, Pat!

It has been so long since I took ANY programming language and theory courses that I had forgotten the "coupling and cohesion" terms - though I used them conceptually. It had become so ingrained that their use became automatic and at that level, you don't name the principles. You just use them. Thanks for the reminder of the nomenclature, too, Pat.
 
Some of you are taking this way too seriously. If I didn't know better, I'd think you were arguing about surrogate vs natural keys:)

You probably don't need another opinion but I thought I should share my original problem with globals which actually was fixed by A2007 or possibly A2010 and that problem was that Global variables used to loose their value if the app encountered an unhandled error.

That problem has not been fixed, Pat (as of A2013 that I am using). But as I pointed out earlier, it only exists if the app is not compiled into .accde.


I don't know about you, but I don't bother to put 10 lines of error trapping code into procedures that have only a few lines of code UNLESS, I know the procedure has the potential to raise an error. Of course I use extensive error trapping when I know errors will be raised but how likely are you to get an error when you have 10 lines of code that are clearing search boxes? So, since this was prior to TempVars, my solution was to create a hidden form where all "global" variables were defined as controls. The upside of this turned out to be that making the form visible during testing enabled me to monitor the variables as they changed and to actually step in and change values to alter logic paths when necessary. That in particular made it much easier to test the application security since I didn't have to keep loging out and logging back in as a different user.

I am not clear about the "upside". Are you saying that this could not be done with global variables without the form ? Eg. via the watch window ? I am not really clear about the benefit of spawning a form just to house a bunch of variables. Why not to put them in a parameter table?

So, even today, in the era of TempVars I still primarially use the form method. If I do use TempVars or Globals, I ALWAYS define them in a single standard module to make it easy for me to find them. My use of "globals" (any of the three methods) is very limited and as someone else already mentioned usually limited to things relating to the logged in user and security.

I personally see nothing wrong in using them also to pass parametric data between modules, in defining application settings, and in managing a session. Also, I am not sure why you would use globals in a standard module when you went into the trouble of creating a hidden form for them. But I guess that's just me.

I know that we all think that we write clean, clear, concicse code and that anyone who reads it will have no trouble understanding it. I hate to burst your bubble but when we are writing an app, we have so much internal information that simply never makes it into coments or documentation that those who follow in our footsteps are frequently at a loss as to why certain design decisions were made. Judicious use of comments helps (that doesn't mean you should comment every single line of code. Use common sense if you want people to actually read the comments and make sure to change them if the procedure's logic changes.). Sometimes documentation overviews help - assuming people actually read them but more important is using standards that will help people who come in cold, figure out what is going on. For example, it is important that you name your globals and TempVars and Form contols in such a was that the average reader can actually "see" their use and so be wary of changing them.

No argument there. If I write code to be maintained by someone else, I make a habit of commenting the correct use of the global variable in the special standard module I put them in. But in general I am convinced that code with globals is more readable and maintanable than one that relies on the use of hidden forms (or Windows API) to pass data.

Knowing he is dealing with a Global will probably give your successor pause for thought and he should realize that changing it could easily affect some other part of the app and so he needs to do some research prior to making any changes.

But, Pat, isn't that the case with pretty much everything? It may be an obscure column in a table; it can be a user-defined structure or object, it can be the use of an event. Why to single out globals? I don't know how you, but when I go over someone's code I don't start changing things until I feel reasonably sure I have solid grasp of what is going on. And actually, I have gone through professional work where all globals where prefixed with "g" or "gl" and placed in a separate module with other globals. So, against all the dire warnings of the naysayers, there is I believe a lot of common sense out there in using the globals.

Pat Hartman said:
Coupling and Cohesion are terms that predate OOP. Coupling describes the connections BETWEEN objects and Cohesion describes the connections WITHIN an object. The general rule is that you want procedures to be loosely coupled (not dependent on each other unnecessarily including passing very few variables) and highly cohesive (all the code within the procedure should relate to the same process). For example, you would never use the same procedure to write payroll checks and FTP your backups because both happen every other Monday. That means that Global variables which are not precicsely defined and correctly used can cause pathalogicol coupling causing procedureB to fail because someone made a change to a variable that was intended to benefit procedureA. I'm sure you've all run into a software bug and while scratching your head said, how the *** did that change cause that effect? The answer will very likely come down to the developer not understanding (or ignoring) the principles of coupling and cohesion. Keep in mind that very few applications go their entire productive lives managed by their original developer. This is why defensive programming is so necessary. Your goal as a developer should't be to be "elegant", it should be to prevent future errors if possible.

This type of argument against the use of globals is why I opened the post. To expose it as group-think or prejudice. It's an argument from "excessive design" that I have not yet seen supported by a single compelling example. Also, it is interesting to see you joining Mark and Galaxiom when you said this earlier:

Pat Hartman said:
]Some of you are taking this way too seriously. If I didn't know better, I'd think you were arguing about surrogate vs natural keys:)

So frankly, I don't think there is danger of "pathological coupling" from the use of global variables. I don't see any reason to label poor programming that way but more to the point, the globals in and of themselves do not present any more of a problem than say object variables. Maybe, there is more issues with the former but that would be because startup programmers usually don't do OOP. But bottom line: you don't prove anything by simply asserting something, especially not when you start from a false premise (i.e. that global variables include an invitation to programmers to use them thoughtlessly and/or in preference to a lesser scope).

Interesting that no-one has pointed out in the nearly three dozen posts here that Access is, for better or worse, a RAD tool. And yet nearly everyone would concede that globals are as a rule the simpler solution. Surely, there has to be some reason why Microsoft insist on leading us into temptation. (Worse even, the old "global variables" were extended into "public variables" in standard modules -A2003?- so you can now choose how to sin). But friends, I am done here so I won't speculate on what it is.

Thanks to all who participated here.

Best,
Jiri
 
This type of argument against the use of globals is why I opened the post. To expose it as group-think or prejudice.
Isn't this statement prejudicial? This is not a thread about globals, it's a prejudicial thread about others' prejudices; a religious thread, by design.
#amirite
Mark
 
From Pat:
I get called in to clean up lots of messes.

For about the first 5 to 10 years of my stay with the U.S. Dept. of Defense, my biggest job was cleaning up other people's messes. Which is probably why both Pat and I use globals when needed, but with trepidation because of the dangers inherent. Solo, you will say that this is group-think or prejudice. I must respectfully - but emphatically - tell you that it is called experience.

In 40+ years of computing in private and public environment, I have learned a few things about the fine art of software engineering. You don't only code something to work - but you code it to work well and you code it to be re-usable because otherwise you are pitching dollars down the "wishing it would work" wishing well. It has been my experience that you must constrain the urge to globalize too many things.

Solo, your viewpoint as you express it is, I am sure, heartfelt and in your mind it must seem clear. To my way of seeing it, you are arguing in favor of practices that I have seen to be expensive (in the "time is money" sense of that term.)

You KNOW if you read my posts that I use globals - but only when carefully designed and limited in subject matter. Please understand that I am not saying to never use globals. But I am saying that they are like having feral cats as pets - they will do their business where you least expect it and stink up the place. Not to mention they will attack you when you least expect it. Whereas if you have a place for your globals, it's like a litter box, a place to contain the stink. OK, I use colorful metaphors sometimes, but it's the way I am.

One thing I will say that is important here is simply this: It's a poor tool kit that doesn't have very many tools in it. Globals are just one way to track things. Tempvars are another. And the problem with having too few tools in the kit? If all you have is a hammer, everything you've got gets nailed.
 
I've never used global variables in a database application. Why would you need a bunch of values sitting in memory when you can load them in, unless you're on a very poor network?

I don't think there's any excuse for unhandled errors, especially if you know you're going to release an MDE/ACCDE.
Trying to sell program crashes as a solution to poor coding is just silly.
 

Users who are viewing this thread

Back
Top Bottom