tags:

views:

124

answers:

3

Hi!

I have a Player class in a separate unit as follows:

TPlayer = class
private
  ...
  FWorld: TWorld;
  ...
public
  ...
end;

I also have a World class in a separate unit as follows:

TWorld = class
private
  ...
  FPlayer: TPlayer;
  ...
public
  ...
end;

I have done it this way so that the Player can get data from the world via FWorld, and so that the other objects in the world can get the player data in a similar manner.

As you can see this results in a circular reference (and therefore does not work). I have read that this implies bad code design, but I just can't think of any better other way. What could be a better way to do it?

Cheers!

+5  A: 

Every once in a while this is called for, and then you do it like this:

//forward declaration:
TWorld = class;

TPlayer = class
private 
  FWorld: TWorld;
public
end;

TWorld = class
private
  FPlayer: TPlayer;
public
end;
500 - Internal Server Error
Does that really work when you're not declaring a pointer? I haven't used Pascal in forever.
Mark Ransom
Yes, that works. It's possible that I'd won't work with plain Pascal, but in that case you wouldn't have classes to begin with.
DR
Yes, it works. Don't foprget that class types *are* pointers in Delphi :)
Mihai Limbășan
@Mark: Yes, it does work, as long as they are both declared within the same TYPE section (so putting another TYPE keyword between both will not work.
dummzeuch
A: 

Put both classes into one unit, or extract an abstract base class of one of the classes, that does not reference the other class. Then you reference this abstract base class in the other class definition:

uses UAbstractPlayer;
TWorld = class
...
private
  FPlayer: TAbstractPlayer;
...

if you create TPlayer in TWorld you can reference the original TPlayer unit in the implementation use clause. Otherwise you don't need to reference the original TPlayer unit at all.

This is called dependency inversion.

Ozan
+3  A: 

Hi,

Just like Ozan said : most of the time, a good answer is to create a base class with virtual methods :

unit BaseWorld;
TBaseWorld = class
  function GetWorldInfo() : TWorldInfo; virtual; {abstract;}
...

unit Player;
TPlayer = class
  FWorld : TBaseWorld;
  constructor Create( AWorld : TBaseWorld );
...

unit RealWorld;
TWorld = class(TBaseWorld)
  function GetWorldInfo() : TWorldInfo; override;
  ...

TWorld.AddPlayer();
begin
  TPlayer.Create(Self);
end;
...

or, with a similar effect, publish an interface :

unit WorldIntf;
IWorldInterface = interface
  function GetWorldInfo() : TWorldInfo;
...

unit Player;
TPlayer = class
  FWorld : IWorldInterface;
  constructor Create( AWorld : IWorldInterface );
...

unit RealWorld;
TWorld = class(TInterfacedObject, IWorldInterface)
  function GetWorldInfo() : TWorldInfo;
  ...

TWorld.AddPlayer();
begin
  TPlayer.Create(Self);
end;
...

Depending on how your code works, you may want to hide the World behind an abstract layer (as in the above examples) or the Player (as suggested by Ozan).

LeGEC
+1 I would prefer to abstract the World too, because the World probably knows all about the Player, but the Player doesn't know everything about the World (like about the Monster hiding behind that tree :-)
Incredulous Monk