如何链接“平行”类层次结构? [英] How to link "parallel" class hierarchy?

查看:243
本文介绍了如何链接“平行”类层次结构?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类层次结构,每个类对应一个特定的TComponent后代(分别为基类TDefaultFrobber,后代为TActionFrobber和TMenuItemFrobber,分别对应于TComponent,TCustomAction和TMenuItem)。现在我想要一个这样的工厂(?)函数:

 函数CreateFrobber(AComponent:TComponent):IFrobber; 
begin
如果AComponent是TCustomAction然后
结果:= TActionFrobber.Create(TCustomAction(AComponent))
如果AComponent是TMenuItem然后
结果:= TMenuItemFrobber.Create (TMenuItem(ACComponent))
else
结果:= TDefaultFrobber.Create(ACComponent);
结束

我可以以某种方式重构这个来使用虚拟函数或类似的东西而不是if-else级联或RTTI ?



编辑:我现在的解决方案:

 <$单位Frobbers; 

接口

使用
类;

type
IComponentFrobber = interface
end;

TComponentFrobberClass = TComponentFrobber类;

TComponentFrobber = class(TInterfacedObject,IComponentFrobber)
strict private
FComponent:TComponent;
protected
构造函数Create(AComponent:TComponent);
属性Component:TComponent读取FComponent;
public
类函数FindFrobberClass(AComponentClass:TComponentClass):TComponentFrobberClass;超载;静态的;
类函数FindFrobberClass(AComponent:TComponent):TComponentFrobberClass;超载;静态的;
类程序RegisterFrobber(AComponentClass:TComponentClass; AFrobberClass:TComponentFrobberClass);静态的;
结束

执行

使用
ActnList,
菜单;

type
TComponentFrobberRegistryItem = record
ComponentClass:TComponentClass;
FrobberClass:TComponentFrobberClass;
结束

var
FComponentFrobberRegistry:TComponentFrobberRegistryItem的数组;

类函数TComponentFrobber.FindFrobberClass(AComponentClass:TComponentClass):TComponentFrobberClass;
var
i:整数;
begin
//向后搜索,以便首先找到更专业的抽奖者:
for i:= High(FComponentFrobberRegistry)downto Low(FComponentFrobberRegistry)do
如果FComponentFrobberRegistry [i] .ComponentClass = AComponentClass then
begin
结果:= FComponentFrobberRegistry [i] .FrobberClass;
退出;
结束
结果:= nil;
结束

构造函数TComponentFrobber.Create(AComponent:TComponent);
开始
继承创建;
FComponent:= AComponent;
结束

类函数TComponentFrobber.FindFrobberClass(AComponent:TComponent):TComponentFrobberClass;
var
i:整数;
begin
//向后搜索,以便首先找到更专业的抽奖者:
for i:= High(FComponentFrobberRegistry)downto Low(FComponentFrobberRegistry)do
如果AComponent是FComponentFrobberRegistry [ i] .ComponentClass then
begin
结果:= FComponentFrobberRegistry [i] .FrobberClass;
退出;
结束
结果:= nil;
结束

类程序TComponentFrobber.RegisterFrobber(AComponentClass:TComponentClass;
AFrobberClass:TComponentFrobberClass);
var
i:整数;
begin
Assert(FindFrobberClass(AComponentClass)= nil,'Duplicate Frobber class');
i:= Length(FComponentFrobberRegistry);
SetLength(FComponentFrobberRegistry,Succ(i));
FComponentFrobberRegistry [i] .ComponentClass:= AComponentClass;
FComponentFrobberRegistry [i] .FrobberClass:= AFrobberClass;
结束

函数CreateComponentFrobber(AComponent:TComponent):IComponentFrobber;
var
FrobberClass:TComponentFrobberClass;
begin
FrobberClass:= TComponentFrobber.FindFrobberClass(AComponent);
Assert(FrobberClass<> nil);
结果:= FrobberClass.Create(AComponent);
结束

type
TActionFrobber = class(TComponentFrobber);
TMenuItemFrobber = class(TComponentFrobber);

初始化
TComponentFrobber.RegisterFrobber(TCustomAction,TActionFrobber);
TComponentFrobber.RegisterFrobber(TMenuItem,TMenuItemFrobber);
结束。

感谢Cesar,Gamecat和mghie。

解决方案

2建议:
创建类对数组的类,然后可以获取索引并使用一对类构造函数,

  var 
TComponentClass =(TActionFrobber,TMenuItemFrobber)的ArrayItem:array [0..1];
ArrayOwner:TComponentClass =(TCustomAction,TMenuItem)的数组[0..1];

函数CreateFrobber(AComponent:TComponentClass):IFrobber;
var
索引:整数;
begin
结果:= nil;
for I:= Low(ArrayOwner)to High(ArrayOwner)do
如果AComponent是ArrayOwner [I]然后
begin
结果:= ArrayItem [I] .Create(AComponent );
休息;
结束

如果Result = nil then
结果:= TDefaultFrobber.Create(AComponent);
结束

或使用RTTI + ClassName约定,如下所示:

 函数CreateFrobber(AComponent:TComponentClass):IFrobber; 
const
FrobberClassSuffix ='Frobber';
var
LClass:TComponentClass;
LComponent:TComponent;
begin
LClass:= Classes.FindClass(AComponent.ClassName + FrobberClassSuffix);
如果LClass nil then
LComponent:= LClass.Create(AComponent)
else
LComponent:= TDefaultFrobber.Create(ACComponent);

如果不支持(LComponent,IFrobber,Result)然后
结果:= nil;
结束


I've got a little class hierarchy where each class corresponds to a certain TComponent descendent (say base class TDefaultFrobber with descendents TActionFrobber and TMenuItemFrobber, corresponding to TComponent, TCustomAction and TMenuItem, respectively). Now I want a factory (?) function something like this:

function CreateFrobber(AComponent: TComponent): IFrobber;
begin
  if AComponent is TCustomAction then
    Result := TActionFrobber.Create(TCustomAction(AComponent))
  else if AComponent is TMenuItem then
    Result := TMenuItemFrobber.Create(TMenuItem(AComponent))
  else
    Result := TDefaultFrobber.Create(AComponent);
end;

Can I somehow refactor this to use virtual functions or something similar instead of the if-else cascade or RTTI?

Edit: My solution for now:

unit Frobbers;

interface

uses
  Classes;

type
  IComponentFrobber = interface
  end;

  TComponentFrobberClass = class of TComponentFrobber;

  TComponentFrobber = class(TInterfacedObject, IComponentFrobber)
  strict private
    FComponent: TComponent;
  protected
    constructor Create(AComponent: TComponent);
    property Component: TComponent read FComponent;
  public
    class function FindFrobberClass(AComponentClass: TComponentClass): TComponentFrobberClass; overload; static;
    class function FindFrobberClass(AComponent: TComponent): TComponentFrobberClass; overload; static;
    class procedure RegisterFrobber(AComponentClass: TComponentClass; AFrobberClass: TComponentFrobberClass); static;
  end;

implementation

uses
  ActnList,
  Menus;

type
  TComponentFrobberRegistryItem = record
    ComponentClass: TComponentClass;
    FrobberClass: TComponentFrobberClass;
  end;

var
  FComponentFrobberRegistry: array of TComponentFrobberRegistryItem;

class function TComponentFrobber.FindFrobberClass(AComponentClass: TComponentClass): TComponentFrobberClass;
var
  i: Integer;
begin
  // Search backwards, so that more specialized frobbers are found first:
  for i := High(FComponentFrobberRegistry) downto Low(FComponentFrobberRegistry) do
    if FComponentFrobberRegistry[i].ComponentClass = AComponentClass then
    begin
      Result := FComponentFrobberRegistry[i].FrobberClass;
      Exit;
    end;
  Result := nil;
end;

constructor TComponentFrobber.Create(AComponent: TComponent);
begin
  inherited Create;
  FComponent := AComponent;
end;

class function TComponentFrobber.FindFrobberClass(AComponent: TComponent): TComponentFrobberClass;
var
  i: Integer;
begin
  // Search backwards, so that more specialized frobbers are found first:
  for i := High(FComponentFrobberRegistry) downto Low(FComponentFrobberRegistry) do
    if AComponent is FComponentFrobberRegistry[i].ComponentClass then
    begin
      Result := FComponentFrobberRegistry[i].FrobberClass;
      Exit;
    end;
  Result := nil;
end;

class procedure TComponentFrobber.RegisterFrobber(AComponentClass: TComponentClass;
  AFrobberClass: TComponentFrobberClass);
var
  i: Integer;
begin
  Assert(FindFrobberClass(AComponentClass) = nil, 'Duplicate Frobber class');
  i := Length(FComponentFrobberRegistry);
  SetLength(FComponentFrobberRegistry, Succ(i));
  FComponentFrobberRegistry[i].ComponentClass := AComponentClass;
  FComponentFrobberRegistry[i].FrobberClass := AFrobberClass;
end;

function CreateComponentFrobber(AComponent: TComponent): IComponentFrobber;
var
  FrobberClass: TComponentFrobberClass;
begin
  FrobberClass := TComponentFrobber.FindFrobberClass(AComponent);
  Assert(FrobberClass <> nil);
  Result := FrobberClass.Create(AComponent);
end;

type
  TActionFrobber = class(TComponentFrobber);
  TMenuItemFrobber = class(TComponentFrobber);

initialization
  TComponentFrobber.RegisterFrobber(TCustomAction, TActionFrobber);
  TComponentFrobber.RegisterFrobber(TMenuItem, TMenuItemFrobber);
end.

Thanks to Cesar, Gamecat and mghie.

解决方案

2 suggestions: Make class pair array of classes, then you can get the Index and use the pair of the class constructor,

var
  ArrayItem: array[0..1] of TComponentClass = (TActionFrobber, TMenuItemFrobber);
  ArrayOwner: array[0..1] of TComponentClass = (TCustomAction, TMenuItem);

function CreateFrobber(AComponent: TComponentClass): IFrobber;
var
  Index: Integer;
begin
  Result:= nil;
  for I := Low(ArrayOwner) to High(ArrayOwner) do
    if AComponent is ArrayOwner[I] then
    begin
      Result:= ArrayItem[I].Create(AComponent);
      Break;
    end;

  if Result = nil then
    Result:= TDefaultFrobber.Create(AComponent);
end;

or use RTTI + ClassName conventions, like this:

function CreateFrobber(AComponent: TComponentClass): IFrobber;
const 
  FrobberClassSuffix = 'Frobber';
var
  LClass: TComponentClass;
  LComponent: TComponent;
begin
  LClass:= Classes.FindClass(AComponent.ClassName + FrobberClassSuffix);
  if LClass <> nil then 
    LComponent:= LClass.Create(AComponent) 
  else
    LComponent:= TDefaultFrobber.Create(AComponent);

  if not Supports(LComponent, IFrobber, Result) then
    Result:= nil;
end;

这篇关于如何链接“平行”类层次结构?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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