July 02, 2007
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.
July 02, 2007
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
July 03, 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...

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
July 03, 2007
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.
>> 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?

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.
July 03, 2007
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.
July 03, 2007
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
July 03, 2007
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.
1 2
Next ›   Last »