2010-04-29 87 views
4

我有一个单独的单元中的播放器类,如下所示:循环参考修复?

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

我也有一个世界级的独立单元如下:

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

我已经做到了这种方式,使玩家可以通过FWorld从世界获得数据,并且世界上的其他物体也可以以类似的方式获取玩家数据。

正如你可以看到这个结果是一个循环引用(因此不起作用)。我已经读过,这意味着糟糕的代码设计,但我想不出任何其他更好的方法。什么可能是更好的方法来做到这一点?

干杯!

回答

6

每隔一段时间,这就是所谓的,然后你做这样的:

//forward declaration: 
TWorld = class; 

TPlayer = class 
private 
    FWorld: TWorld; 
public 
end; 

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

当你没有声明一个指针时,这真的起作用吗?我没有永远使用帕斯卡。 – 2010-04-30 00:59:26

+1

是的,那是有效的。有可能我不会使用简单的Pascal,但在这种情况下,你不会有课程开始。 – 2010-04-30 07:22:46

+0

是的,它的工作原理。不要使用Delphi中的类指针类* * – 2010-04-30 08:24:45

0

把两个班为一个单元,或提取抽象基类的类之一的,但这不参考其他班级。然后你在其他类定义引用这个抽象基类:

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

如果您在t世界创造TPlayer可以执行使用子句中引用原始TPlayer单元。否则,你根本不需要参考原始的TPlayer单元。

这被称为dependency inversion

4

就像Ozan说:大部分的时候,一个很好的答案是创建虚拟方法的基类:

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

根据代码的工作方式,您可能希望将世界隐藏在抽象层(如上面的示例中)或Player(如Ozan所建议的)后面。

+0

+1我更喜欢抽象世界,因为世界可能知道玩家的全部内容,但玩家并不知道关于世界的所有事情(比如关于隐藏在树后面的怪物:-) – 2010-05-06 02:06:39