views:

61

answers:

2

Most of the programs I write are relatively flowchartable processes, with a defined start and hoped-for end. The problems themselves can be complex but do not readily lean towards central use of objects and event-driven programming. Often, I am simply churning through great varied batches of text data to produce different text data. Only occasionally do I need to create a class: As an example, to track warnings, errors, and debugging message, I created a class (Problems) with one instantiation (myErr), which I believe to be an example of the Singleton design pattern. As a further factor, my colleagues are more old school (procedural) than I and are unacquainted with object-oriented programming, so I am loath to create things they could not puzzle through.

And yet I hear, again and again, how even the Singleton design pattern is really an anti-pattern and ought to be avoided because Global Variables Are Bad.

Minor functions need few arguments passed to them and have no need to know of configuration (unchanging) or program state (changing) -- I agree. However, the functions in the middle of the chain, which primarily control program flow, have a need for a large number of configuration variables and some program state variables. I believe passing a dozen or more arguments along to a function is a "solution," but hardly an attractive one. I could, of course, cram variables into a single hash/dict/associative array, but that seems like cheating.

For instance, connecting to the Active Directory to make a new account, I need such configuration variables as an administrative username, password, a target OU, some default groups, a domain, etc. I would have to pass those arguments down through a variety of functions which would not even use them, merely shuffle them off down through a chain which would eventually lead to the function that actually needs them.

I would at least declare the configuration variables to be constant, to protect them, but my language of choice these days (Python) provides no simple manner to do this, though recipes do exist as workarounds.

Numerous Stack Overflow questions have hit on the why? of the badness and the requisite shunning, but do not often mention tips on living with this quasi-religious restriction. How have you resolved, or at least made peace with, the issue of global variables and program state? Where have you made compromises? What have your tricks been, aside from shoving around flocks of arguments to functions?

+2  A: 

I think that there is a time and a place for the singleton pattern, or similar situations. The key point to remember is that time and time again, many people have experienced specific horror when it comes to the "wrong" choice to use global/shared/static variables as well as the singleton pattern.

In your case, you are talking about configuration specifically. I see no harm in using a singleton style pattern for the access to those configuration items. Every application has configuration, it SHOULD be in a location that you can just call, there is no need to just pass it around, that complicates more than it helps.

The key here is to make sure that you TRULY need the information to exist only once, configuration is by far one of the best reasons that I have found to use this type of pattern.

Mitchel Sellers
+1  A: 

Global data that is unchanging, or well-defined, process-wide objects (e.g., logging), are generally OK to use as global data. Configuration data, especially if it lives in a local file, would fall under the same category (there's just one file for the whole process/application, similar to logging).

In general, if you find you have to pass in extra arguments just to pass them to another function, you should pull that function "up" and have another that mediates. Another, more practical, approach that illustrates this is test-driven development because it forces you to pass in dependencies.

Another way of thinking about it is: if that function can't easily know all the details to call a sub-function, pull the sub-function up, and force a higher, more knowledgable, layer to acquire the needed information. I've found this technique to be highly conductive to code quality, as it builds compartmentalized pieces, instead of monolithic beasts.

In your example about active directory, instead of passing around the arguments to the ad_connect, pass around an object that handles the necessary logic, then have a function that mediates the interaction between the that object and the function using it

def create_user(name, ad_user, ad_pass, ad_ou, ...):
   conn = ad_connect(ad_user, ad_pass, ...)
   conn.CreateRecord(ad_user)

def create_user_mediator(name, ad_controller):
  ad_controller.CreateRecord("cn=%s" % name)

Thats just one way of doing it, and, of course, has its pros and cons. Its merely an example of how create_user can avoid having to use global variables.

Richard Levasseur