One of the first things they taught me in my college Pascal class (I already knew some Pascal, but had to take it for the credit...on the bright side, that's where I learnt about pointers and recursion, so it was worth it...) was that global variables are bad. And in most cases this is true, but I think it's easy to take this philosophy too far.
Suppose you're designing a game architecture. And so you have classes for the app, the sound engine, the graphics engine, the input device, the game world. These are all singletons. Nothing's stopping you from making them globals or using some sort of singleton pattern (where they're still effectively global; any class can access them from anywhere). But say you say to yourself, "I know globals are bad. I'm going to have all of these singletons be members of App, and App will initialize and deinitialize them in the proper order."
Now you're stuck with a couple of possibilities:
Everything that wants to refer to one of these classes has to talk to App. They're still effectively global if you do this - anyone can access them from anywhere. But you've also incurred the overhead of your App .h file including all your other .h files, and you'll probably start to find that a lot of your .h and .cpp files are including App.h, giving you a nasty nest of circular dependencies, where whenever you change an .h file you're rebuilding half the program.
Or, you can do what Grady Booch suggests: when you instantiate a class that needs to refer to one of your singletons, you pass the instance a pointer to your singleton. Yay, no globals and you can use forward declaration to prevent .h files including .h files. What you do have, though, is Duplication of State. Supposing you later decide you want to be able to destroy and re-instantiate your singleton, perhaps to reset it or for some other purpose. Imagine all the bugs that could introduce. Besides, it's just plain ugly bulking all your classes up with all those pointers. I've never seen an engine carry this through to the full extreme - I've only seen engines where it seemed like some coders preferred philosophy A while others tried philosophy B.
If you just made those singletons global, or used some variation of a popular singleton pattern, then you can make sure that only the .cpp files that use your singleton need to include its .h files, and your code doesn't get as gunked up.
1. Instantiate beans with various information and attach them to the global session object, where they are available if needed, and not if not. Java doesn't use header files, so you're all good there.
2. Use Java :)
Okay, yes, I understand Java isn't (yet) fast enough -- but it almost is, and for many applications, it probably actually is fast enough now, as well as being a LOT LESS system dependent than C++. I write games in various flavors of Python, and there's very little speedy about THAT language (though I have written some bits in C++). Isn't EVE Online written in Stackless Python?
Maybe it's time for the game industry to look more into the possibility of using slightly more modern and portable languages?
Posted by: Tipa | July 23, 2009 at 03:30 AM
I have you say, I completely disagree, Jamie.
Let me get one easy one out of the way: Having an App.h file is NOT going to make any appreciable difference. Having one thousand of those is probably not going to make much of a difference with proper forward declarations and header guards (just don't include templates, STL, or Windows.h).
But apart from that, I despise gobals and go out of my way to avoid having them in my code. Not out of a sense of "proper software engineering" (that's long gone) but simply because globals get in the way.
They get in the way of optimizations (harder to cleanly replace some code)
They get in the way of testing (no way to test without those globals)
They get in the way of refactoring
They get in the way of understanding the program flow.
And... the biggest of all if the globals are data... they get in the way of the data flow. Now you call a function and you have no idea what it is going to affect in memory. I prefer to have a very clear input data and output data parameters. You can easily take that and vectorize it, parallelize, schedule it, or whatever we have to do these days with our fancy hardware.
So yeah, you can say I feel pretty strongly that globals are evil :-)
Posted by: Noel | July 23, 2009 at 04:01 AM
Oh wait, I'm not done! (and the first line of the previous comment was supposed to be "I have TO say" BTW).
Singletons vs. globals.
Another huge problem with globals is initialization time. You have no guarantees about what else (globally) has been initialized. That's not purely academic, it's a big deal if you start needing graphics, or sound, or network, or the log system or...
Singletons with auto-initialization and shutdown are pretty pointless, and more counterproductive than anything.
If you really, absolutely must have singletons, please use explicit ones. I guess at that point it's no different than new and delete the global. Just don't use global variables that are actual class instances!
Posted by: Noel | July 23, 2009 at 04:05 AM
I'm with Noel on this one, particularly on testability. Please do yourself a favor and see Misko Hevery's info on this. He's a little Java centric, but he gets the point across.
http://www.youtube.com/watch?v=-FRm3VPhseI
http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/
Posted by: Patrick | July 23, 2009 at 06:27 AM
It's not about global state. Ith's about how that state is accessed and where it's accessed from.
This debate raged for the last 5 years on the web: RESTful vs RPC.
RESTful won.
When designing a service these are the principles that you want to take into account:
- client/server
- layered
- cacheable
- resource based
- stateless protocol (VERY, VERY important)
- support CRUD type operations consistently and uniformly
All of this makes your code easier to read, testable, etc. And most importantly, it makes it will work in the many-core future.
More info here:
http://en.wikipedia.org/wiki/Representational_State_Transfer
http://www.xfront.com/REST-Web-Services.html
Posted by: Parveen Kaler | July 23, 2009 at 10:23 AM
I used to believe like Jamie but on my latest project someone introduced me to the Service Locator pattern and it's been pretty awesome.
No globals, much less dependencies, easy to test things in isolation.
Originally we effectively had this App class that had a ton of dependencies and almost everything depended on it.
Now we have 10-12 mini classes separated by responsibility. Most of them depend on almost nothing and each higher level thing in our app only needs to include the small interface class headers for the particular services they need.
The only thing that has to be passed around is a pointer to the ServiceLocator or that can be the only global (as it is in our unit_tests).
Posted by: greggman | July 23, 2009 at 08:07 PM
Oh, btw, because of this ServiceLocator we don't have any Singletons. The advantage to this is for example in a unit test, like I mentioned before we do have a global service locator with a few services on it that lots of tests need so those tests will do something like
Renderer* renderer = g_serviceLocator->GetService();
But, even with the global service locator, in many tests we can create another service locator, add some services do it and the tests in that unit will use that one as in
// Create a new service locator
ServiceLocator localServiceLocator;
// Add a service to our local service locator.
MockRendererer mockRenderer<&localServiceLocator>;
// Call some test with our local serivce locator.
DoTest(&localServiceLocator);
...
void DoTest(ServiceLocator* serviceLocator) {
Renderer* renderer = serviceLocator->GetService();
... // Do something with the renderer.
}
Since DoTest takes a pointer to a SerivceLocator we can hand it the global one or one with our MockRenderer and it will find the one we want it to find.
I wish I could point you to more easy to follow examples but our code is open source if you want to browse
http://src.chromium.org/viewvc/chrome/trunk/src/o3d/core/cross/?dir_pagestart=150
service_locator.h is the class to find services.
service_implementation.h defines a class you put in another class if you want that class to be a Service.
service_dependency.h is a class that helps deal with to services that are dependent on each other.
Otherwise, features.h, renderer.h, object_manager.h, class_manager.h, error_manager.h, event_manager.h and others are all examples of services.
Those and about 10 others all used to be in client.h
If you want instructions on how to build see
http://o3d.googlecode.com
Otherwise, this pattern is apparently also called Dependency Injection but I don't know any good articles on it. The one on wikipedia is way to abstract.
Posted by: greggman | July 23, 2009 at 08:30 PM
Grr, your blog ate all my less than signs.
The GetService lines should look like
serviceLocator->GetService<IRenderer>();
Posted by: greggman | July 23, 2009 at 08:33 PM