Style/Structuring question: One vs. multiple global objects

Deewiant deewiant.doesnotlike.spam at gmail.com
Sun Jul 1 09:09:12 PDT 2007


Henning Hasemann wrote:
> Say I have a game engine in which nearly all parts including the
> engines user need to access some global values.
> Example for such would be the screen width & height, the current main
> character, the currently displayed scene, the jukebox-object which does
> sound stuff, etc...
> 
> I used to have one single global game object which stores all the other
> things mentioned above because deep in my mind there's a "global
> variables are evil" burned in.
> 
> Now where the stuff is getting more complex, this often leads to some
> "forward reference" issues because the game module needs to import a
> lot (scene module, character module, etc...) but also lots of modules
> need game.
> So this leads to circular imports which somehow leads to that forward
> reference stuff.
> 
> Would it be a better idea to have multiple global objects for the
> different things? I.e. a "currentScene" global in the scene module, a
> "mainCharacter" global in the character module etc...?
> 
> What would you do?
> 

I'd start with making sure "lots of modules need game" == false, to the point
that no modules need game. IMHO if you have a kind of "god object" (or module, I
don't write objects for such things), nobody should know about its existence.
Just like real-life religion. ;-)

If you're really that paranoid about globals (I see no reason to be), try to
make something which contains only the external interfaces to your object and
put it in a separate module.

After that, try to cleverly eliminate all circular imports in the rest of the
program, which probably means creating new modules (i.e. instead of A <-> B, A
-> C and B -> C) in addition to just moving stuff around. In general, I prefer
creating modules, because it makes the code clear: the alternative is being
forced to put something in a module where it clearly doesn't belong.

A possible problem with the above is that you may end up with several very short
modules which only contain a few globals. When it looks like this is happening,
and I can't find a justifiable way of merging them, I tend to just create a
"globals.d".

Overall it's not that simple a problem. I find that allowing circular imports is
often an optimization: you can circumvent certain limitations without them, but
it's easier to just import.

For instance, in a game, you have a Tile object and an Entity object. A Tile
needs to know of the Entities standing on it and the Entity needs to know where
it's standing. There are, as far as I can think of, three ways to deal with this:
	
- Put Tile and Entity in the same module, which leads to a "tile_and_entity.d"
with potentially thousands of lines of code. Not very nice.
	- Succumb to the inevitable and use a circular import, which may lead to
forward reference issues and makes the built-in code coverage analyser unusable.
	- Store Entity in Tile, and store x- and y-coordinates in Entity. They can then
be used by modules higher up in the hierarchy to pull the Tile out from an
array, or Map object, or whatever.

When I mention optimization, I'm referring to how the former two solutions allow
to store Entity and Tile normally in each other, and how the last solution adds
one or more layers of indirection. It's a bit ugly, and it slows things down a
bit, but it works.

Another option might be "partial class implementation" as mentioned by davidl
recently on the digitalmars.D newsgroup. Object, for instance, is declared in
object.d but implemented in internal\object.d. (Here object.d should really be
.di since it's only an interface.) Unfortunately, I have no idea how to get this
to work: I always seem to get linker errors.

And we can't do what C++ does with its header files (a handy way of avoiding
circular imports):

struct S { int f(); }

int S.f() { return 1; } // like C++'s S::f, but not in D

In my experience, D's abandoning of header files might not have been such a good
idea, since especially in games, as you have noticed, there tends to be a lot of
coupling between seemingly unrelated things, which makes it very hard to program
clean code in D without circular imports. Or then I've just missed something
which does make it easy.

Hopefully this helps.

-- 
Remove ".doesnotlike.spam" from the mail address.


More information about the Digitalmars-d-learn mailing list