2010-04-29 4 views
4

J'ai une classe de joueur dans une unité séparée comme suit:Correction de référence circulaire?

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

J'ai aussi une classe mondiale dans une unité séparée comme suit:

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

Je l'ai fait de cette façon afin que la Le joueur peut obtenir des données du monde via FWorld, et ainsi les autres objets dans le monde peuvent obtenir les données du joueur d'une manière similaire.

Comme vous pouvez le voir, cela donne une référence circulaire (et par conséquent ne fonctionne pas). J'ai lu que cela implique une mauvaise conception de code, mais je ne peux pas penser à un autre meilleur moyen. Quelle pourrait être une meilleure façon de le faire?

À la votre!

Répondre

6

Chaque fois que dans un certain temps cela est appelé, et vous le faites comme ceci:

//forward declaration: 
TWorld = class; 

TPlayer = class 
private 
    FWorld: TWorld; 
public 
end; 

TWorld = class 
private 
    FPlayer: TPlayer; 
public 
end; 
+0

Est-ce que cela fonctionne vraiment quand vous ne déclarez pas un pointeur? Je n'ai pas utilisé Pascal depuis toujours. –

+1

Oui, cela fonctionne. Il est possible que je ne travaillerai pas avec Pascal, mais dans ce cas, vous n'auriez pas de cours pour commencer. –

+0

Oui, cela fonctionne. Ne pas foprend que les types de classe * sont * des pointeurs dans Delphi :) –

0

Mettre les deux classes en une seule unité, ou un extrait d'une classe de base abstraite de l'une des classes, qui ne ne référence pas l'autre classe. Ensuite, vous faites référence à cette classe de base abstraite dans l'autre définition de la classe:

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

si vous créez TPlayer en TWORLD vous pouvez référencer l'unité de TPlayer d'origine dans la clause d'utilisation de la mise en œuvre. Sinon, vous n'avez pas besoin de référencer l'unité TPlayer d'origine.

Ceci est appelé dependency inversion.

4

Tout comme Ozan dit: la plupart du temps, une bonne réponse est de créer une classe de base avec des méthodes virtuelles:

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

ou, avec un effet similaire, publier une 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; 
... 

Selon la façon dont votre code fonctionne, vous pouvez vouloir cacher le monde derrière un calque abstrait (comme dans les exemples ci-dessus) ou le joueur (comme suggéré par Ozan).

+0

+1 Je préférerais aussi faire abstraction du Monde, car le Monde sait probablement tout sur le Joueur, mais le Joueur ne sait pas tout sur le Monde (comme sur le Monstre qui se cache derrière cet arbre :-) –

Questions connexes