The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Does anyone program like this in C++?

I have bene writing in C++ for a short time, and recently came across the power of using singletons and static variables to manage my variables across class structures. In fact, I stuck in a lot of my app parameters as a bunch of static variables and static functions, all located within a class called ProgramState. Then I make regular called to stuff such as ProgramState::IsThingActive. I find this highly useful, clear and easy to manage, but I was wondering if this is the good way to attack such a problem.

Thursday, December 23, 2004
 
 
It sounds a lot like stateLESS web programming where you throw in everything into session bags and query them later.

Personally, I hate these kind of programming styles.  The way I see it, (if I understand you correctly), it seems like a new way of using global variables everywhere.  On the other hand, what do I know; it might be a good idea for the problem you are trying to solve (unlikely).
RM
Thursday, December 23, 2004
 
 
Basically you are using global variables.

But I really don't have a problem with this sort of use of global variables.
Dennis Atkins
Thursday, December 23, 2004
 
 
So long as you only have a single state for the process, i.e. its not providing services, then it isn't much of a problem.  If you encapsulate them all into a singleton object then if you do need to provide different states its not a huge leap to convert that singleton class into one which is instantiated separately for each caller.
Simon Lucy Send private email
Thursday, December 23, 2004
 
 
If I get you right your code looks like this:

class NeatLittleFunctionProvider
{
public:
void doNeatThing
{
  printf("Neat Thing");
  if (ProgramState::isLoggingEnabled)
    log("Neat Thing Done"); 
}

void main()
{
  NeatLittleFunctionProvider f;
  f.doNeatThing();
}

Depending on the type of your project, this may cause problems, since it introduces dependencies, as was already mentioned. Dependencies can easily kick you in the ass: http://www.baus.net/archives/000135.html

A more clean design looks like this:

class NeatLittleFunctionProvider
{
public:
void doNeatThing(bool log)
{
  printf("Neat Thing");
  if (log)
    log("Neat Thing Done"); 
}

void main()
{
  NeatLittleFunctionProvider f;
  f.doNeatThing(ProgramState::IsLoggingEnabled);
}

NeatLittleFunctionProvider now is independend from any other class and the functions doNeatThing() can be easily tested against all code paths.

When designing a class, you should ask yourself about the minimum of "knowledge" the class must have. ProgramState is a class that acts on application level, does NeatLittleFunctionProvider needs to know there is something like an application that has states? In most cases, you will figure out, that it doesn't. This knowledge should be reserved for top level classes that stir behaviour.

Or, as Christopher says it in the article referenced above: "Thinks as a library developer".
Gerd Riesselmann
Thursday, December 23, 2004
 
 
Aside from the issues already raised, I'd like to chat a bit about static member variables and the lifetimes of global objects.

For example, consider a window manager which keeps a map of window handles to objects available as a static member variable, which you can indirectly access via Manager::CreateAssoc(h, p).  When the window is destroyed, it calls Manager::DestroyAssoc(h).  Likewise, when the object to which a window is mapped is destroyed, the object will call Manager::ObjectDestroyed(p).

But you can have a problem if you don't think about destruction order.  If you've got a global window object, does it get destroyed before or after your manager?  If it gets destroyed after, Manager::ObjectDestroyed will try to access its static member variable (the internal static map) causing an access violation.

So I'd advise you to use a single static pointer to your class, set it to NULL when the class is destroyed, and handle the case of a NULL singleton pointer in your static methods.
Kalani Send private email
Thursday, December 23, 2004
 
 
This really goes against the concept of object oriented programming were you want to design classes defined by their interfaces that provide services for clients to use.  They would be minimal and as decoupled as possible.  This makes it easier when the applications functionality changes.  A singleton is usually used when you have a class that provides some type of utility that many other classes would like to use.  It can save memory and be more efficient to have a singleton that is shared amongst all the other objects.  However, the other objects don't know that they are sharing this singleton.
John Jenkins Send private email
Thursday, December 23, 2004
 
 
I'd agree with the consensus so far - that this is global variables cleverly wrapped. Gerd's excellent comments about dependancies is probably most important here. If you're writing small programs, it's probably not going to hurt too bad if you need to make changes. I won't comment on whether it's right or wrong, but I will say that there are other ways of going about it, because it's not the approach I find myself taking.

What you might try is putting the data in the state class closer to the object that owns it. For your ProgramState::IsThingActive example, is there a Thing object you can give an IsActive() method to?

Gerd's doNeatThing function is a good prototype to work from. I think it's worth noting that, as a first factorisation step, you might want to consider passing the program vector right into the functions that use it.

So, instead of

void DoThis()
{
  if (settings.boolvar)  // global
  {
    // Do something conditional
  }
}

You do something like this instead;

void DoThis(Settings settings)
{
  if (settings.boolvar)  // local
  {
    // Do something conditional
  }
}

You should be able to do that refactoring pretty quickly. (If you can't, it shows the extent of your code's interdependancy) The nice thing about this step is that it ensures that wherever you use a variable, you've explicity passed it into the function. That means you can see from the call what might be affected or used by the function.

A second step would be to try to reduce access to the Settings class. So, since the function above only uses a single boolean property, just pass that it. It starts to look like Gerd's function;

void DoThis(bool condition)
{
  if (condition)
  {
    // Do something conditional
  }
}

It's worth noting here that, for a fresh reader of the code, this function is trivial to understand. For the first version (using globals) you have to understand about this global/singleton class.

Anyway, that hopefully is a not too tough path for moving away from globals, if you want to.

1) Move the data closer
2) pass the settings into each function that uses it
3) if the function only uses one or two properties from the settings class, pass the properties explicitly
Steve Cooper Send private email
Friday, December 24, 2004
 
 
If you think of singletons as a service interface that may have factory functionality then i like them a lot. That's far better, IMHO, then passing around parameters everywhere or binding a whole program into one big blob by passing around context objects. It's in the code so it's obvious. You don't need to use a whole new infrastructure like Spring to inject dependencies. Libraries can be compiled in and be kept independent as needed.

Your use of ProgramState isn't good. ProgramState isn't a service. As people said, it is just a dump for your global variables. There is no abstraction or isolation going on. There is no service being offered. It will only make sense to your application and can't be used by any other application you may write.
son of parnas
Friday, December 24, 2004
 
 
Thanks for all your inputs... I have actually now refined what I originally had to the following:

int varUsername, varUserId, varType...;

typedef struct
{
  int  &pos;
  const TCHAR *tagName;
  const TCHAR *initValue;
} myStringVariable;

static myStringVariable StringVariables[NUM_VARS] =
{
    { varUsername, "Username", "Default" },
    { varUserId, "Id", "0" },
    { varType, "Type", "Normal" }
    ...
}

CString UserVariables[NUM_VARS];

** In my initialization I have something like this:

for (int i=0; i<NUM_VARS; i++) {
    StringVariables[i].pos = i;
    UserVariables[i] = StringVariables[i].initValue;
}

** And to manage the variables:

void SetVariable(int n, char *val) {
    UserVariables[n] = val;
}

CString GetVariable(int n) {
    return UserVariables[n];
}


So can you see what's happening? My initialization gives an index to each of my variables (varUserName=0, varUserId=1...) and I can then use this index to do stuff such as SetVariable(varUserName, "David").

But most importantly, and the main purpose of this code, I can easily do the following:
* Add new variables easily. Just create a new 'int' variable and add it to StringVariables.
* Enumerate variables to XML for storage. This is a simple 'for' loop that dumped each variable and its associated tag.


Whew. Sorry for the long mess. However, I am still wondering if there's a beter way to do this stuff. You see, all the above code is actually global to the program, because these are parameters that run throughout. OO guys hate this, but then again these are actually global variables used in all my code, so what's the issue? Moreso, I am curious again if there's a better way to do this, noting that my method allows me to easily add new variables and to enumerate them to XML.

Friday, December 24, 2004
 
 
> Whew. Sorry for the long mess.

My inner asshole wants to come out, but i'll try and keep her in check.

May i suggest that you not worry about xml. Instead make your domain objects and then worry about how to import them and then export them to XML. Lookup serialization and deserialization. Making your code easy for XML makes it complete crap for any other purpose.

* use classes, don't use typedefs. Don't use ints. Then each class can serialize itself to XML.
* when programming in OO you for the most part don't care about your "program." You care about classes that do domain kind of things. The domain is absent in your program. Using static may work for your "program" but it won't work in any other program.
* initialization belongs in class constructors
* your approach gets rid of types completely in your program. Why bother using C++? Everything is a useless string with no behaviour except what you manage to scatter around your program. Those silly OO guys like to have behaviour and data in the same place, a class, so it can be understood, extended, inhereted from, etc.
*
son of parnas
Friday, December 24, 2004
 
 
Good God. Don't use TCHAR *, don't use CString. Use the standard library.

    Flava Flav!
Flava Flav!
Friday, December 24, 2004
 
 
I replied to this topic in another thread before I read this one. I think that it is appropriate to use singletons to store configuration settings, although I wouldn't usually have a single configuration object. The comments about removing dependencies are good - the configuration classes may depend on a lot of other classes so you want to avoid having too many classes depend on it. I find most of my apps these days have only a few classes where everything comes together and this is the only place that would have a direct dependency on the configuration classes.

Anyay, I can't quite understand why you would want singletons AND static variables. If you use the singleton correctly you shouldn't need any static variables (apart from one pointer to the singleton).Everything should be stored in the usual class member variables. That's one of the big advantages of singletons - the decision to make the data single-instance and quasi-global is localised into a small place (the singleton) and is easier to change your mind. And no need to initialise all those static members.
Peter Davidson Send private email
Saturday, December 25, 2004
 
 
Sounds to me like you're still using a global system. You asked;

"OO guys hate this, but then again these are actually global variables used in all my code, so what's the issue?"

First up, it's not an OO thing, but a procedural programming thing from waaay back in the past - probably the same kinda time that GOTO was proved redundant.

I think the easiest way I can explain it is to give you an example. Think about the situation if I were helping you program, and you'd asked me to write a sort function for you, which did both alpha sorting (putting '9' after '10') and natural ordering (putting '10' after '9').

Now, if I make the sort algorithm a global setting, then you call it like this;

  Sort(list);

And the behaviour is varied based on the (invisible) global variable. If you write

  Sort(list, naturalOrder);

it's much clearer what method will be used. Also, consider wanting to do different sorts on different lists. I can call

  Sort(list1, naturalOrder);
  Sort(list2, alphaOrder);

which is impossible if it's a global variable; this makes globally-scoped stuff less flexible.

Also consider that, with a global variable, anyone writing code for the program can modify it, and you get a hellish time if you involve more than one person in the writing. Going back to our co-operation; I could write a piece of code which changes the sort algorithm global variable, and your code - the code that calls Sort() - suddenly behaves differently. And the kicker is, your code hasn't changed.

So now, I can screw up your code. You can't rely on your code being good, because your code is handcuffed to mine and if mine is shonkey, yours is too.
Steve Cooper Send private email
Tuesday, January 11, 2005
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz