TInterfaced
或 TInterfacedPersistent
的类中添加和实现一个接口,以此将模型和视图分开为两个单元?我需要这样做的一个简要解释是:我正在开发一个树形结构的开放式模型,其结构如下(非常简化和不完整,只是为了说明问题的概要):Database_Kernel.pas。TVMDNode = class(TInterfacedPersistent);
public
class function ClassGUID: TGUID; virtual; abstract; // constant. used for RTTI
property RawData: TBytes {...};
constructor Create(ARawData: TBytes);
function GetParent: TVMDNode;
function GetChildNodes: TList<TVMDNode>;
end;
厂商特定的东西.pas
TImageNode = class(TVMDNode)
public
class function ClassGUID: TGUID; override; // constant. used for RTTI
// Will be interpreted out of the raw binary data of the inherited class
property Image: TImage {...};
end;
TUTF8Node = class(TVMDNode)
public
class function ClassGUID: TGUID; override; // constant. used for RTTI
// Will be interpreted out of the raw binary data of the inherited class
property StringContent: WideString {...};
end;
TContactNode = class(TVMDNode)
public
class function ClassGUID: TGUID; override; // constant. used for RTTI
// Will be interpreted out of the raw binary data of the inherited class
property PreName: WideString {...};
property FamilyName: WideString {...};
property Address: WideString {...};
property Birthday: TDate {...};
end;
使用基于 GUID 的运行时类型信息(RTTI)(使用 ClassGUID
),函数GetChildNodes
能够找到匹配的类并使用原始数据进行初始化。(每个数据集都包含ClassGUID
和RawData
,以及其他数据,如创建/更新时间戳)。
重要的是要注意,我的API(Database_Kernel.pas
)严格与供应商的节点类(Vendor_Specific_Stuff.pas
)分开。
供应商特定程序的GUI希望可视化节点,例如为它们提供用户友好的名称、图标等。
以下思路可行:
IGraphicNode = interface(IInterface)
function Visible: boolean;
function Icon: TIcon;
function UserFriendlyName: string;
end;
Vendor_Specific_Stuff.pas
中 TVMDNode
的特定子类将实现 IGraphicNode
接口。
但是厂商还需要更改 Database_Kernel.pas
,将 IGraphicNode
实现到基础节点类 TVMDNode
上(该类用于“未知”节点,即 RTTI 无法找到数据集的匹配类,因此可以使用 TVMDNode.RawData
读取二进制原始数据)。
他将更改我的类如下:
TVMDNode = class(TInterfacedPersistent, IGraphicNode);
public
property RawData: TBytes {...};
class function ClassGUID: TGUID; virtual; abstract; // constant. used for RTTI
constructor Create(ARawData: TBytes);
function GetParent: TVMDNode;
function GetChildNodes: TList<TVMDNode>;
// --- IGraphicNode
function Visible: boolean; virtual; // default behavior for unknown nodes: False
function Icon: TIcon; virtual; // default behavior for unknown nodes: "?" icon
function UserfriendlyName: string; virtual; // default behavior for unknown nodes: "Unknown"
end;
问题在于IGraphicNode
是特定于供应商/程序的,不应该出现在API的Database_Kernel.pas
中,因为GUI和Model/API应该严格分离。我的愿望是将接口
IGraphicNode
添加和实现到现有的TVMDNode
类中(它已经是TInterfacedPersistent
的子类,以允许接口),并放入一个单独的单元中。据我所知,Delphi不支持这样的方法。除了在一个单一的单元/类中混合Model和View之外,还存在以下真实问题:如果供应商必须更改我的
Database_Kernel.pas
API以扩展TVMDNode
与IGraphicNode
接口,则他需要重新进行所有更改,一旦我发布了新版本的API Database_Kernel.pas
。我该怎么办?我考虑了很久使用Delphi OOP可能的解决方案。一种解决方法可能是将TVMDNode嵌套到一个容器类中,该容器类具有次要的RTTI,因此在找到
TVMDNode
类之后,我可以搜索TVMDNodeGUIContainer
类。但这听起来非常奇怪,像个肮脏的hack。PS:这个API是一个OpenSource/GPL项目。我正在尝试保持与旧一代Delphi(例如6)的兼容性,因为我想最大化可能的用户数量。然而,如果解决上述问题的方法只能通过新一代的Delphi语言实现,我可能会考虑放弃对此API的Delphi 6支持。
TGraphicNode = class(TVMDNode)
放在 'vendor_specific_stuff.pas' 中和将IGraphicNode = interface(IInterface)
与TVendorVMDNode = class(TVMDNode, IGraphicNode)
放在 'vendor_specific_stuff.pas' 中的区别。可能是我弄丢了,你不必在意。 - Sertac AkyuzIGraphicNode
或使用共享派生类TGraphicVMDNode
。这两种方法都适用于所有供应商的扩展。唯一的问题是当TVMDNode.GetChildNodes
中的RTTI算法无法找到确切的类时,它会使用TVMDNode.Create
作为最后的手段来实例化一个对象。这个对象不是供应商的TGraphicVMDNode
的派生类,因此无法在程序的GUI中显示。 - Daniel Marschall