软件设计:Delphi中类层次结构的适配器(泛型与向下转换)

软件设计:Delphi中类层次结构的适配器(泛型与向下转换),delphi,generics,inheritance,software-design,downcast,Delphi,Generics,Inheritance,Software Design,Downcast,我想就以下问题提出一些建议: 假设您想为VCL控件编写适配器。所有适配器都应该具有相同的基类,但在包装特殊控件方面有所不同(例如,从TEdit获取值与从TSpinEdit获取值不同)。 因此,第一个想法是创建一个类层次结构,如 TAdapter = class end; TEditAdapter = class (TAdapter) end; TSpinEditAdapter = class (TAdapter) end; 现在我想引入一个字段来保存对vcl控件的引用。在我的特殊Adapa

我想就以下问题提出一些建议: 假设您想为VCL控件编写适配器。所有适配器都应该具有相同的基类,但在包装特殊控件方面有所不同(例如,从TEdit获取值与从TSpinEdit获取值不同)。 因此,第一个想法是创建一个类层次结构,如

TAdapter = class
end;

TEditAdapter = class (TAdapter)
end;

TSpinEditAdapter = class (TAdapter)
end;

现在我想引入一个字段来保存对vcl控件的引用。在我的特殊Adapater中,我当然希望使用具体的子类。但是基类还应该包含一个引用(例如,如果我想使用适配器使控件可见)

可能性1(属性访问器中的向下转换):

因此,如果我实现了一个特定的方法,我使用属性控件,如果我在基类中做了一些事情,我使用protected字段

可能性2(使用泛型基类):

TAdapter=class
结束;
TAdapter=类(TAdapter)
受保护的
FCtrl:T;
结束;
TEditAdapter=类(TAdapter)
结束;
您喜欢哪种解决方案?还是有第三种更好的解决方案

亲切问候,


Christian

泛型版本可以避免一些重复的代码,至少在TAdapter类中是这样。通过使用
T
类型,它将允许大量共享代码

另一方面,由于VCL的层次结构,大多数使用的属性和方法将已经在
t控件中了。所以我不确定在非泛型实现中会有这么多重复的代码

我怀疑非泛型版本将产生更少的代码和RTTI,因为当前泛型实现倾向于不复制源代码,而是增加exe大小

基于泛型的设计将为实现增加更多的抽象,但非泛型设计可能更接近它将适应的VCL层次结构

因此,对于您的特定情况(映射VCL),由于您的尝试是映射非泛型类,因此我宁愿研究非泛型解决方案


对于另一种(非基于VCL的)适配器体系结构,我可能会建议从下到上使用纯泛型实现。

您不能使用泛型来解决此问题,因为您将处于以下两种情况之一:

  • 要“调整”的属性或方法(例如
    文本
    属性)是在祖先类中定义的。在这种情况下,您不需要泛型,因为您可以对祖先使用一个适配器,并解决所有子代的问题
  • 属性或方法由要调整的类引入。在这种情况下,您不能使用泛型,因为为了访问属性或方法,您需要该给定类型的泛型类型约束。例子。假设您想要为
    TMyClass
    Text
    属性使用适配器。我们假设
    TMyClass
    是引入
    Text
    属性的。为了访问它,您需要将泛型类型声明为
    TGeneric
    ,而这实际上不是泛型的

在我看来,最好的办法是为每个类编写特定的适配器,就像在第一个选项中一样。您可能可以使用RTTI技巧使第一个选项更易于实现,但我不确定它是否值得。

我不明白这一点:
“但是基类还应该包含引用(例如,如果我想使用适配器使控件可见)”
;什么是基类?您希望修改后的类包含对适配器的引用吗?基类是TAdapter-很抱歉出现这种混淆。引用是类型为TControl(第一种情况)或类型为T(第二种情况)的字段。“但基类也应包含引用”…请注意,在第二个版本中,基类
TAdapter
不包含对控件的引用。此外,没有一个泛型基类,只有多个泛型基类:
TAdapter,TAdapter,…
不幸的是,TControl包含大多数属性和方法。。。举个例子:OnChange事件。我知道它对从TControl继承的任何控件都没有用处——但这对我的应用程序来说并不重要——如果有OnChange事件,我希望在适配器中使用它。如果没有,我不在乎。甚至没有一个TControl子类具有一个OnChange事件。组合、编辑或列表视图有不同的OnChange事件。这就是我需要这些适配器的原因——如果VCL设计得更好一点,这甚至不是必需的;)@Christian,使用简单的RTTI可以轻松处理具有通用名称的属性。看看
TypInfo
单元,它包含一些简单的例程,允许检查给定的已发布属性是否存在,以及根据名称获取和设置大多数类型属性的函数。@Cosmin我经常使用RTTI,在XE中甚至有更好的接口来使用它。但我认为,这会减慢我的申请。。。我们有包含大约700个或更多控件的表单。我仍然不知道,我想有它的文本属性吗?还是它的价值属性?或者我有像TMS SpinEdit一样的东西——我想要一个FloatValue吗?日期值?DateTimeValue?好的,我可以做一些名称映射。。。也许我会试一试,但我想,它会慢一些+RTTI为1approach@Christian,RTTI的速度惊人,尤其是如果缓存所有相关的RTTI数据结构,这样就不用花时间为每个函数调用挖掘它们。我将RTTI用于定制对象关系映射器,而且速度很快。RTTI的速度对我来说从来都不是问题。此外,如果您不能使用继承,并且不想为每个类编写自定义代码,则只剩下RTTI。@Cosmin:缓存
trtti属性时,RTTI的速度真是让我吃惊。它的速度只比它慢2.5倍
TAdapter = class
protected
  FCtrl : TControl;
end;

TEditAdapter = class (TAdapter)
  public
    property Control : TEdit read GetControl write Setcontrol;
end;
{...}
function TEditAdapter.GetControl : TEdit;
begin
  Result := FCtrl as TEdit;
end;
TAdapter = class
end;

TAdapter <T : TControl> = class (TAdapter)
protected
  FCtrl : T;
end;

TEditAdapter = class (TAdapter <TEdit>)
end;