之后添加一个接口到一个类 [英] Add an interface to a class afterwards

查看:160
本文介绍了之后添加一个接口到一个类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以添加并实现一个已经存在的类的接口(它是 TInterfaced TInterfacedPersistent )完成将Model和View分为2个单元?



一个小的解释为什么我需要这样的东西:



我正在开发一个树型结构的开放式模型,它具有以下结构(非常简单和不完整,仅用于说明问题的大纲):



Database_Kernel.pas

  TVMDNode = class(TInterfacedPersistent); 
public
class function ClassGUID:TGUID;虚拟;抽象; // 不变。用于RTTI

属性RawData:TBytes {...};
构造函数Create(ARawData:TBytes);

函数GetParent:TVMDNode;
函数GetChildNodes:TList< TVMDNode> ;;
结束

Vendor_Specific_Stuff.pas

  TImageNode = class(TVMDNode)
public
类函数ClassGUID:TGUID;覆盖// 不变。用于RTTI

//将被解释为继承类的原始二进制数据
属性Image:TImage {...};
结束

TUTF8Node = class(TVMDNode)
public
类函数ClassGUID:TGUID;覆盖// 不变。用于RTTI

//将被解释为继承类的原始二进制数据
属性StringContent:WideString {...};
结束

TContactNode = class(TVMDNode)
public
类函数ClassGUID:TGUID;覆盖// 不变。用于RTTI

//将被解释为继承类的原始二进制数据
属性PreName:WideString {...};
属性FamilyName:WideString {...};
property地址:WideString {...};
property生日:TDate {...};
结束

使用基于GUID的RTTI(使用 ClassGUID ),函数 GetChildNodes 能够找到匹配的类并使用原始数据进行初始化。 (每个数据集包含 ClassGUID RawData 除了其他数据,如创建/更新的时间戳)



重要的是要注意,我的API( Database_Kernel.pas )与供应商的节点类( Vendor_Specific_Stuff)严格分开。 pas )。






供应商特定程序的GUI希望可视化节点,例如给他们一个用户友好的名字,一个图标等。



以下想法:

 code> IGraphicNode = interface(IInterface)
function Visible:boolean;
功能图标:TIcon;
function UserFriendlyName:string;
结束

供应商的特定后代 TVMDNode code> Vendor_Specific_Stuff.pas 将实现 IGraphicNode 界面。



但是供应商还需要更改 Database_Kernel.pas ,以实现 IGraphicNode 到基本节点类 TVMDNode (用于未知节点,其中RTTI无法找到数据集的匹配类,因此至少可以使用 TVMDNode.RawData读取二进制原始数据)。



所以他会改变我的课程如下:

  TVMDNode = class(TInterfacedPersistent,IGraphicNode); 
public
属性RawData:TBytes {...};
类函数ClassGUID:TGUID;虚拟;抽象; // 不变。用于RTTI
构造函数Create(ARawData:TBytes);
函数GetParent:TVMDNode;
函数GetChildNodes:TList< TVMDNode> ;;

// --- IGraphicNode
function Visible:boolean;虚拟; //未知节点的默认行为:False
function图标:TIcon;虚拟; //未知节点的默认行为:? icon
function UserfriendlyName:string;虚拟; //未知节点的默认行为:未知
end;

问题是 IGraphicNode 是供应商/程序特定,不应该在API的 Database_Kernel.pas 中,因为GUI和Model / API应该严格分开。



我的愿望是,可以将现有的 TVMDNode 类添加并实现interace IGraphicNode 已经是 TInterfacedPersistent 的后代允许接口)在一个单独的单位。据我所知,Delphi不支持这样的东西。



除了在一个单一的单元/类中混合模型和视图不是很好,将会遇到真正的问题:如果供应商必须更改我的 Database_Kernel.pas API以将 TVMDNode IGraphicNode 界面,他需要重新执行所有的更改,一旦我发布了一个新版本的我的API Database_Kernel.pas



我该怎么办?我非常想,可能的解决方案可能与德尔福的OOP。解决方法可能是将TVMDNode嵌套到容器类中,该类具有辅助RTTI,因此在找到 TVMDNode 类之后,我可以搜索 TVMDNodeGUIContainer 类。但是这听起来很扼杀,就像一个肮脏的黑客。



PS:这个API是一个OpenSource / GPL项目。我试图与旧版Delphi保持兼容(例如6),因为我希望最大化可能的用户数量。然而,如果以上问题的解决方案只能用新一代Delphi语言,我可能会考虑删除Delphi 6对此API的支持。

解决方案

如果您要应用工厂设计模式



对于每个节点类,将有一个类工厂一个函数指针)负责创建正确的Delphi类。类工厂可以在单元初始化部分(每次应用程序启动一次)在内核单例对象注册。



然后,内核单例会将GUID映射到正确的工厂,转动调用正确的类实例构造函数(如 http://delphipatterns.blog .com / 2011/03/23 / abstract-factory )



软件包可能会被拆分成单独的DLL,并以不同的单元实现,仍然继承自基础TVMNode类。



您现在可以使用RTTI的功能可以通过一些虚拟方法轻松地支持后代类或工厂类。



您还可以考虑使用更简单的数据传输对象用于保存/加载TVMNodes,也可能从a获得灵感已经很好地了解了对象关系映射器对象持久性框架作为您正在尝试解决的问题,正如我们正在处理的问题(已经)



我不知道这个类的好的Delphi开源框架。但是从其他语言,您可以查看 Java Hibernate Microsoft .NET实体框架或简约 Google协议缓冲区序列化程序




Is it possible to add and implement an interface to an already existing class (which is a descendant of TInterfaced or TInterfacedPersistent) to accomplish separating Model and View into 2 units?

A small explanation why I need something like this:

I am developing a tree-structure, open-type model, which has following structure (VERY simplified and incomplete, just to illustrate the outline of the problem):

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;

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

Using a GUID-based RTTI (which uses ClassGUID), the function GetChildNodes is able to find the matching class and initialize it with the raw data. (Each dataset contains ClassGUID and RawData beside other data like created/updated timestamps)

It is important to notice that my API (Database_Kernel.pas) is strictly separated from the vendor's node classes (Vendor_Specific_Stuff.pas).


A vendor-specific program's GUI wants to visualize the nodes, e.g. giving them an user-friendly name, an icon etc.

Following idea works:

IGraphicNode = interface(IInterface)
  function Visible: boolean;
  function Icon: TIcon;
  function UserFriendlyName: string;
end;

The vendor's specific descendants of TVMDNode in Vendor_Specific_Stuff.pas will implement the IGraphicNode interface.

But the vendor also needs to change Database_Kernel.pas to implement IGraphicNode to the base node class TVMDNode (which is used for "unknown" nodes, where RTTI was unable to find the matching class of the dataset, so at least the binary raw data can be read using TVMDNode.RawData).

So he will change my class as follows:

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;

The problem is that IGraphicNode is vendor/program-specific and should not be in the API's Database_Kernel.pas, since GUI and Model/API should be strictly divided.

My wish would be that the interace IGraphicNode could be added and implemented to the existing TVMDNode class (which is already a descendant of TInterfacedPersistent to allow interfaces) in a separate unit. As far as I know, Delphi does not support something like this.

Beside the fact that it is not nice to mix Model and View in one single unit/class, there will be following real-world problem: If the vendor has to change my Database_Kernel.pas API to extend TVMDNode with the IGraphicNode interface, he needs to re-do all his changes, as soon as I release a new version of my API Database_Kernel.pas.

What should I do? I thought very long about possible solutions possible with Delphi's OOP. A workaround may be nesting TVMDNode's into a container class, which has a secondary RTTI, so after I have found the TVMDNode class, I could search for a TVMDNodeGUIContainer class. But this sounds very strangle and like a dirty hack.

PS: This API is an OpenSource/GPL project. I am trying to stay compatible with old generations of Delphi (e.g. 6), since I want to maximize the number of possible users. However, if a solution of the problem above is only possible with the new generation of Delphi languages, I might consider dropping Delphi 6 support for this API.

解决方案

You can preserve the ability to persist data and implement it through inheritance and still create the correct instances for the ClassGUIDs stored in the tables if you'd apply the factory design pattern.

For each node class there would be one class factory (or just a function pointer) responsible for creation of the correct Delphi class. Class factories may register themselves in the unit initialization section (once per application startup) at the kernel singleton object.

The kernel singleton would then map GUID to correct factory that would in turn call the correct class instance constructor (as shown at http://delphipatterns.blog.com/2011/03/23/abstract-factory)

Packages may be split into separate DLLs and classes implemented in separate units, still inheriting from one base TVMNode class.

The features you now use RTTI for can be supported in descendant classes or in the factory classes easily through some virtual methods.

You might also consider using simpler Data Transfer Objects for saving/loading the TVMNodes and perhaps take some inspiration from an already well perceived Object Relational Mapper or a Object Persistence framework as the problem you are trying to solve seem to me like exactly the problems they are handling (already)

I don't know about good Delphi open source frameworks of this class. But from other languages you can look at Java Hibernate, Microsoft .NET Entity Framework or minimalistic Google Protocol Buffers serializer

这篇关于之后添加一个接口到一个类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆