www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Style/Structuring question: One vs. multiple global objects

reply Henning Hasemann <hhasemann web.de> writes:
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?

Henning


PS:
I even wrote a small python script which tries to find cycles in the
"import graph" which demonstrated me that not every circular import
leads to a forward reference and that I have much more circular imports
that I'd expected.

-- 
GPG Public Key:
http://keyserver.ganneff.de:11371/pks/lookup?op=get&search=0xDDD6D36D41911851
Fingerprint: 344F 4072 F038 BB9E B35D  E6AB DDD6 D36D 4191 1851
Jul 01 2007
next sibling parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
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.
Jul 01 2007
next sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Henning Hasemann wrote:
 I think the paranoia about globals comes from some experiences with or
 hints for other programming languages such as python etc... where
 globals are generally considered bad style, though I must confess I
 never understood why there is/was such a radical talking against them.
 

Globals are much less annoying in D than in Python. They are annoying in Python because of Python's scoping rules. For the benefit of those who don't know Python, the issue is this: i = 12 def foo(): print i # prints '12' i = 20 foo() # call foo print i # prints 12; foo did not change 'i' When it assigns 20 to 'i' inside of foo(), it is actually creating a new name local to foo(), which shadows the global name. To assign to the global name instead, you must use the 'global' keyword. j = 14 def bar(): global j print j # prints '14' j = 40 bar() print j # prints '40'; bar changed 'j' You might expect this to lead to some confusion (particularly among newbies to Python), and you'd be right. It is because of this potentially confusing situation that globals are generally frowned upon in Python. In D, on the other hand, variables have to be explicitly declared. If the only variable with a certain name is at global scope, then using that name will obviously only refer to the global variable: int i; void foo() { i = 12; // changes the global } D goes further by making the shadowing of global variables an error, and making ambiguous situations in general errors. I am much less hesitant to use globals in D than I am in Python. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Jul 01 2007
parent Derek Parnell <derek psych.ward> writes:
On Sun, 01 Jul 2007 13:07:49 -0700, Kirk McDonald wrote:

 I am much less hesitant 
 to use globals in D than I am in Python.

I'm old-school in that my first programming langugage was COBOL. We were taught that globals were to be avoided because it added to the maintenance effort - and I still agree with that. The issue is that if you change a global symbol (either at coding time or at run time), you have to take extra effort at finding out what is affected by that change. By keeping symbols more locally, it is easier to find the dependancies. However, sometimes some things need to be global as they are used in many other modules. For example if your application supports a "-verbose" command line switch. The choice is then whether to expose the variable or an interface to the variable. In other words ... if (Global_IsVerbose) ... or if (Global_IsVerbose() ) ... In either case, I'd recommend naming the entity in such a manner that highlights its 'globalness' effect. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Jul 01 2007
prev sibling parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Henning Hasemann wrote:
 Deewiant <deewiant.doesnotlike.spam gmail.com> schrieb (Sun, 01 Jul 2007 
 19:09:12 +0300):
 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. ;-)

Interesting strategy. But that would mean things like the main character had to be accessible in other ways, or?

Like: struct GodObject { MainCharacter dude; void gameLoop() { engine.actOnInput(dude.getInput()); } } You get the idea. MainCharacter would be in its own module so if something needs its definition, it can import that module, but the "dude" variable itself would always be passed from GodObject.
 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.


 What exactly do you mean with external interface? The point is: the "game" 
 class would need to know about scenes, characters and so on. So it had to 
 import the proper modules. And, for example lots of modules deal somehow with
  the current scene or the current main character so they had to import 
 game... Maybe you could give a simple code example of your strategy?

See above. Pass around the current main character if you have a "struct/class Game", or define it as a global in maincharacter.d if you're willing to use globals.
 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".

That globals.d then would look like the following, describing the same problem as game.d: import mylib.scene; import mylib.viewport; import mylib.character; //... // globals mylib.scene.Scene currentScene; mylib.viewport.Viewport viewport; mylib.character.Character mainCharacter;

This is why I'd put such definitions in the modules their types are defined in, in this case. Generally, it's the kind of types that don't need to import anything else from the project are the ones that'd end up in a globals.d.
 - 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.

You mean: class Tile { // ... class Entity { int x,y; } } ? I guess you mean something different, but atm I just dont get it...

I mean: class Tile { Entity standingOnThis; } Tile[] tiles; // in other module... class Entity { int x, y; } Then, with the above, we can do: void getTileOfEntity(Entity e) { return tiles[e.y * MAP_WIDTH + e.x]; // or whatever } The downside is that you need some sort of "utility module" which imports both Tile and Entity and defines getTileOfEntity. That, or put it in the same module as Tile, which isn't exactly logical.
 Hopefully this helps.

Today at least a little. But I think if I sleep over it and look at it tomorrow again it'll help more ;-) Thanks so far! Henning

Not a problem. If you can think of something, feel free to enlighten me, too. I'd really like a general-purpose solution which doesn't involve all the kinds of hacks we have to resort to now. -- Remove ".doesnotlike.spam" from the mail address.
Jul 02 2007
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Henning Hasemann wrote:
 Well the dependency-inversion thingy I found 
 (http://www.objectmentor.com/resources/articles/dip.pdf) was really 
 enlightning, maybe it helps you too. The basic idea is to misuse interfaces
 to simulate some of the benefits of C++'s header files.

It incurs runtime penalties and makes the code too cluttered, IMHO. One thing I came up with was doing the "tile_and_entity.d" like so: module tile_and_entity; mixin(import(tile.d)); mixin(import(entity.d)); Should work, but quite hacky. -- Remove ".doesnotlike.spam" from the mail address.
Jul 02 2007
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Henning Hasemann wrote:
 Deewiant <deewiant.doesnotlike.spam gmail.com> wrote:
 Henning Hasemann wrote:
 Well the dependency-inversion thingy I found 
 (http://www.objectmentor.com/resources/articles/dip.pdf) was really 
 enlightning, maybe it helps you too. The basic idea is to misuse
 interfaces to simulate some of the benefits of C++'s header files.


How big are those penalties? What are the extra steps your machine has to do when you compare acces through an interface with direct access to an object?

Probably tiny enough to be unnoticeable on even modern cellphones. I'm not sure how inheritance works at the asm level. But it's a matter of principle: when all I want is to remove dependencies, why do I need to create a slew of objects? BTW, I just realized that when I mentioned "external interfaces" in my original response I was alluding to the same kind of abstraction talked about in the dependency inversion article, I just had the wrong terminology and forgot about it later. But I still don't advocate the kind of trash mentioned in the article. For instance, there's code with a Button object and a Lamp object. Fine. But then he goes on to create a ButtonClient and ButtonImplementation object? The hell?! This, IMHO, leads to extremely complicated and obtuse interfaces like the Java Platform APIs, where you have String, StringBuffer, StringBuilder, StringHolder, etc. Or ImageInputStream, ImageInputStreamImpl, ImageInputStreamSpi, and the same ImageOutput*, and ImageReader, ImageWriter, ImageReaderSpi, ImageWriterSpi, ImageReaderWriterSpi, ImageIO. So if you have no clue how to read an image from a file there are 6 classes which might be able to do what you want, based on their names. Besides, for an application it makes less sense to do such than for a library. Why make a ButtonImplementation and ButtonClient if you _know_ there's going to be only one type of Button?
 One thing I came up with was doing the "tile_and_entity.d" like so:

 module tile_and_entity;

 mixin(import(tile.d));
 mixin(import(entity.d));

 Should work, but quite hacky.

Looks like a good idea for such situations with two modules that need each other, but if I understand correctly that wouldnt be able to handle all the things that DIP can, or?

If you put your whole program in one module, it can! <g> I'm not sure, but combined with the other techniques it should help things, because that's the only case I've run into so far which can't really be solved well. How would you do this with DIP? -- Remove ".doesnotlike.spam" from the mail address.
Jul 02 2007
parent Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Henning Hasemann wrote:
 Does putting everything in one module really avoid all forward
 reference issues?

No, it doesn't (and that's a good point, that probably wouldn't actually work). But it solves circular dependencies between modules.
 itile.d:
 interface ITile { }
 
 tile.d:
 import itile;
 import ientity;
 class Tile : ITile { /* use entity via interface here */ }
 
 ientity.d:
 interface IEntity { }
 
 entity.d:
 import ientity;
 import itile;
 class Entity : IEntity { /* use tile here via interface */ }
 
 Yes, for such a case your pattern seems better at least if you dont
 find other reasons for creating IEntity and ITile.

That's the whole point. Entity and Tile are already the absolute base classes, they just happen to need to be aware of each other. The ITile/IEntities are just substitutes for C++ header files. Of course, another way: class Entity { Tile t; } class Tile { Object e; // whenever needed, use cast(Entity)e } This reminds me a bit of pre-generics Java, actually, where the contents of Arrays and the like could only be Objects. (Or something like that: I can't remember exactly.) -- Remove ".doesnotlike.spam" from the mail address.
Jul 03 2007
prev sibling next sibling parent Henning Hasemann <hhasemann web.de> writes:
Deewiant <deewiant.doesnotlike.spam gmail.com> schrieb (Sun, 01 Jul
2007 19:09:12 +0300):
 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. ;-)

Interesting strategy. But that would mean things like the main character had to be accessible in other ways, or?
 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.

I think the paranoia about globals comes from some experiences with or hints for other programming languages such as python etc... where globals are generally considered bad style, though I must confess I never understood why there is/was such a radical talking against them. Thats one of the main reasons for my post: I'm always a little unsure how to deal with globals stylistically. What exactly do you mean with external interface? The point is: the "game" class would need to know about scenes, characters and so on. So it had to import the proper modules. And, for example lots of modules deal somehow with the current scene or the current main character so they had to import game... Maybe you could give a simple code example of your strategy?
 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.

Fully agree here, nearly every class has its own module in my code.
 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".

That globals.d then would look like the following, describing the same problem as game.d: import mylib.scene; import mylib.viewport; import mylib.character; //... // globals mylib.scene.Scene currentScene; mylib.viewport.Viewport viewport; mylib.character.Character mainCharacter; My game.d atm is nearly the same with a "class Game { }" around it plus some simple functions.
 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.

Ack.
 	- 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.

You mean: class Tile { // ... class Entity { int x,y; } } ? I guess you mean something different, but atm I just dont get it...
 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.

Seems so :-(
 Hopefully this helps.

Today at least a little. But I think if I sleep over it and look at it tomorrow again it'll help more ;-) Thanks so far! Henning -- GPG Public Key: http://keyserver.ganneff.de:11371/pks/lookup?op=get&search=0xDDD6D36D41911851 Fingerprint: 344F 4072 F038 BB9E B35D E6AB DDD6 D36D 4191 1851
Jul 01 2007
prev sibling next sibling parent torhu <fake address.dude> writes:
Henning Hasemann wrote:
 PS:
 I even wrote a small python script which tries to find cycles in the
 "import graph" which demonstrated me that not every circular import
 leads to a forward reference and that I have much more circular imports
 that I'd expected.
 

Have you tried this tool? It creates import graphs. http://www.shfls.org/w/d/dimple/
Jul 01 2007
prev sibling next sibling parent Henning Hasemann <hhasemann web.de> writes:
torhu <fake address.dude> schrieb (Mon, 02 Jul 2007 04:01:35 +0200):
 Henning Hasemann wrote:
 PS:
 I even wrote a small python script which tries to find cycles in the
 "import graph" which demonstrated me that not every circular import
 leads to a forward reference and that I have much more circular
 imports that I'd expected.
 

Have you tried this tool? It creates import graphs. http://www.shfls.org/w/d/dimple/

Thanks for that, nice tool. But my graph is just a little to complex to see anything with the naked eye ,-) Henning -- GPG Public Key: http://keyserver.ganneff.de:11371/pks/lookup?op=get&search=0xDDD6D36D41911851 Fingerprint: 344F 4072 F038 BB9E B35D E6AB DDD6 D36D 4191 1851
Jul 02 2007
prev sibling next sibling parent Henning Hasemann <hhasemann web.de> writes:
 Like:
 
 struct GodObject {
 	MainCharacter dude;
 
 	void gameLoop() {
 		engine.actOnInput(dude.getInput());
 	}
 }

Oh, I see we talked about the same thing with different words. The situation is/was roughly like this: import character; class Game { Character mainCharacter; void gameLoop() { // ... } } The point is: When something imports game.d, that in turn will import character which may itself be tempted to import game etc, so here is no further division possible without either splitting the god object "Game" into multiple globals or use the "Dependency inversion" pattern I found yesterday on the page torhu pointed me to (http://www.shfls.org/w/d/dimple/). This pattern seems very useful (I even think it woll solve almost all of my module-structuring problems), but I hesitate to build a structure like this: thing.d: -------- class Thing { // some functionality, that is also needed by character } icharacter.d ------------- interface ICharacter { // ... } character.d: ------------ import thing; import icharacter; class Character : Thing, ICharacter { // ... } game.d: ------- import icharacter; class Game { ICharacter mainCharacter; } In this case I think I will prefer making mainCharacter a simple global variable that can be found in character.d or whatever since the dependency inversion approach (which I love in general) here has a few disadvantages: * unneccessary complicated when it comes to express the relation between character and thing * probably misleading to other coders (ICharacter vs. Character) * there seem to be only few argument relly against a hand full of globals
 See above. Pass around the current main character if you have a
 "struct/class Game", or define it as a global in maincharacter.d if
 you're willing to use globals.

atm I tend to the latter
 This is why I'd put such definitions in the modules their types are
 defined in, in this case. Generally, it's the kind of types that
 don't need to import anything else from the project are the ones
 that'd end up in a globals.d.

Ah okay then your glabals.d scheme can work ;-)
 
 Not a problem. If you can think of something, feel free to enlighten
 me, too. I'd really like a general-purpose solution which doesn't
 involve all the kinds of hacks we have to resort to now.

Well the dependency-inversion thingy I found (http://www.objectmentor.com/resources/articles/dip.pdf) was really enlightning, maybe it helps you too. The basic idea is to misuse interfaces to simulate some of the benefits of C++'s header files. Small example (dont take the methods too serious ;-)) scene.d: import thing; class Scene { Thing myThing; } Scene currentScene; thing.d: import viewport; class Thing { void draw() { /* do sth. for ex. with viewport.width */ }} viewport.d: import scene; class Viewport { void eachFrame() { currentScene.draw(); } } This is a nice cycle, which can easily be broken by abstracting scene with an interface (which also yields the possibility to handle movie sequences similar to interactive scenes with a common interface): new scene.d: import thing; import iscene; class Scene : IScene { // same stuff as before } class Movie : IScene { } iscene.d: // does not need to import thing interface IScene { } Note that this pattern doesnt seem to work if Scene looks like this: class Scene : IScene { Thing getThing(); // everyone uses this method } because then IScene imports thing as well as Scene did so the cycle is not broken here (if you iteratively spread this pattern over all your classes including Thing it should work but then you have lots of unneccessary interfaces and your code would become pretty unreadable). HTH Henning -- GPG Public Key: http://keyserver.ganneff.de:11371/pks/lookup?op=get&search=0xDDD6D36D41911851 Fingerprint: 344F 4072 F038 BB9E B35D E6AB DDD6 D36D 4191 1851
Jul 02 2007
prev sibling next sibling parent BCS <ao pathlink.com> writes:
Reply to Henning,
[...]

If you don't want globals I'd uses static members (a fancy name for globals 
that are attached to something else ;) of the class that the data is most 
closely related to. This might not help with cyclical imports but it does 
cut out having a "god" object (and thus splitting things up and maybe helping 
the cycle thing).
Jul 02 2007
prev sibling next sibling parent Henning Hasemann <hhasemann web.de> writes:
Deewiant <deewiant.doesnotlike.spam gmail.com> wrote:
 Henning Hasemann wrote:
 Well the dependency-inversion thingy I found 
 (http://www.objectmentor.com/resources/articles/dip.pdf) was really 
 enlightning, maybe it helps you too. The basic idea is to misuse
 interfaces to simulate some of the benefits of C++'s header files.

It incurs runtime penalties and makes the code too cluttered, IMHO.

How big are those penalties? What are the extra steps your machine has to do when you compare acces through an interface with direct access to an object?
 One thing I came up with was doing the "tile_and_entity.d" like so:
 
 module tile_and_entity;
 
 mixin(import(tile.d));
 mixin(import(entity.d));
 
 Should work, but quite hacky.

Looks like a good idea for such situations with two modules that need each other, but if I understand correctly that wouldnt be able to handle all the things that DIP can, or? Henning -- GPG Public Key: http://keyserver.ganneff.de:11371/pks/lookup?op=get&search=0xDDD6D36D41911851 Fingerprint: 344F 4072 F038 BB9E B35D E6AB DDD6 D36D 4191 1851
Jul 02 2007
prev sibling next sibling parent reply Lutger <lutger.blijdestijn gmail.com> writes:
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...

First think about if they really need to be global variables. Does you renderer need to access the sound system? Does your window class need to know the main character and that character need to know the screen width and height? All these things you mention do not need to be global.
 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.

Global variables are not evil, but they can be 1) costly in terms of maintenance and bugs and 2) indicative of a less than clear design. Same goes for cyclic dependencies. But, like goto, it's not a hard rule.
 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?
 
 Henning

That would already be better, but these globals can be eliminated altogether. Some of the things I do: - think about what really needs access to these variables, often a lot of code does not use all things you are tempted to make global - isolate these 'globals' in one place and let other parts use that. For example screen width and height can be used by the window class and then some select few classes depend on the window class. Problem solved. - use abstractions such as interfaces when appropiate
Jul 02 2007
parent Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Lutger wrote:
 First think about if they really need to be global variables. Does you
 renderer need to access the sound system? Does your window class need to
 know the main character and that character need to know the screen width
 and height? All these things you mention do not need to be global.

Not global per se, but they can be module-level variables with the "package" privacy specifier. Thus you can have foo.sound.globals which can only be accessed by the sound system. -- Remove ".doesnotlike.spam" from the mail address.
Jul 02 2007
prev sibling parent Henning Hasemann <hhasemann web.de> writes:
Deewiant <deewiant.doesnotlike.spam gmail.com> wrote:
 But I still don't advocate the kind of trash mentioned in the
 article. For instance, there's code with a Button object and a Lamp
 object. Fine. But then he goes on to create a ButtonClient and
 ButtonImplementation object? The hell?! This, IMHO, leads to

Okay, fully agreed. I wouldnt not follow this pattern with *that* enthusiasm. Atm I try to find as few cases as possible where it is needed to break my cycles *and* I try to only create interfaces that have a meaning that is distinguishable from the original class in meaning as well as in naming. For example, before I had: class Thing { } class Character : Thing { } now it will become: interface ISceneEntity { } class Thing : ISceneEntity { } class Character : ISceneEntity { } (which works since there is nearly no functionality in Thing)
 extremely complicated and obtuse interfaces like the Java Platform
 APIs, where you have String, StringBuffer, StringBuilder,
 StringHolder, etc. Or ImageInputStream, ImageInputStreamImpl,
 ImageInputStreamSpi, and the same ImageOutput*, and ImageReader,
 ImageWriter, ImageReaderSpi, ImageWriterSpi, ImageReaderWriterSpi,
 ImageIO. So if you have no clue how to read an image from a file
 there are 6 classes which might be able to do what you want, based on
 their names.

Ack. Thats something nobody wants. (Except the javaists maybe)
 Besides, for an application it makes less sense to do such than for a
 library. Why make a ButtonImplementation and ButtonClient if you
 _know_ there's going to be only one type of Button?

Another ack, but what I'm writing is a library ;-)
 If you put your whole program in one module, it can! <g>

Does putting everything in one module really avoid all forward reference issues?
 I'm not sure, but combined with the other techniques it should help
 things, because that's the only case I've run into so far which can't
 really be solved well.
 
 How would you do this with DIP?

itile.d: interface ITile { } tile.d: import itile; import ientity; class Tile : ITile { /* use entity via interface here */ } ientity.d: interface IEntity { } entity.d: import ientity; import itile; class Entity : IEntity { /* use tile here via interface */ } Yes, for such a case your pattern seems better at least if you dont find other reasons for creating IEntity and ITile. -- GPG Public Key: http://keyserver.ganneff.de:11371/pks/lookup?op=get&search=0xDDD6D36D41911851 Fingerprint: 344F 4072 F038 BB9E B35D E6AB DDD6 D36D 4191 1851
Jul 03 2007