2010-04-29 2 views
4

У меня есть класс игрока в отдельном блоке следующим образом:Циркулярное исправление ссылки?

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

У меня есть класс мира в виде отдельного блока следующим образом:

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

Я сделал это таким образом, так что Игрок может получать данные из мира через FWorld, так что другие объекты в мире могут получить данные игрока аналогичным образом.

Как вы можете видеть, это приводит к циркулярной ссылке (и, следовательно, не работает). Я читал, что это подразумевает плохой дизайн кода, но я просто не могу думать о другом. Что может быть лучшим способом сделать это?

Cheers!

ответ

6

Каждый раз в то время это называется для, а затем сделать это следующим образом:

//forward declaration: 
TWorld = class; 

TPlayer = class 
private 
    FWorld: TWorld; 
public 
end; 

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

Действительно ли это работает, если вы не объявляете указатель? Я не использовал Паскаля навсегда. –

+1

Да, это работает. Вполне возможно, что я не буду работать с простым Pascal, но в этом случае у вас не было бы классов для начала. –

+0

Да, это работает. Не foprget, что типы классов * являются указателями * в Delphi :) –

0

Положите оба класса в один блок, или извлечь абстрактный базовый класс одного из классов, что делает не ссылаться на другой класс. Тогда вы ссылаетесь этот абстрактный базовый класс в другом определении класса:

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

если вы создаете TPlayer в TWorld вы можете ссылаться на оригинальный блок TPlayer в пункте использования реализации. В противном случае вам вообще не нужно ссылаться на исходный блок TPlayer.

Это называется dependency inversion.

4

Так же, как Озан сказал: большая часть времени, хороший ответ, чтобы создать базовый класс с виртуальными методами:

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

или, с подобным эффектом, опубликовать интерфейс:

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

В зависимости от того, как работает ваш код, вы можете скрыть мир за абстрактным слоем (как в приведенных выше примерах) или с игроком (как было предложено Озаном).

+0

+1 Я бы предпочел также отвлечь мир, потому что мир, вероятно, знает все о Игроке, но Игрок не знает всего о Мире (например, о монстре, скрывающемся за этим деревом :-) –