【问题标题】:Generic factory looping通用工厂循环
【发布时间】:2016-03-20 07:16:23
【问题描述】:

我想弄清楚如何在 XE2 中编写通用工厂。假设我有这个:

type
  TObjectTypes = (otLogger, otEmail);

type
  TLoggerTypes = (lFile, lConsole, lDatabase);

type
  TEmailTypes = (etPOP3, etSMTP);

类:

TSMTPEmail = class(TInterfacedObject, IEmail); // Supports emailing only
TPOP3Email = class(TInterfacedObject, IEmail); // Supports emailing only
TFileLogger = class(TInterfacedObject, ILogger); // Supports logging only

等等

现在我这样做是为了遍历所有 TObjectTypes:

procedure TForm1.FormCreate(Sender: TObject);
var
  _Interf: IInterface;
  _Configuration: TDictionary<string, TValue>;
  _ObjectType: TObjectTypes;
begin
  _Configuration := nil;
  _Configuration := TDictionary<string, TValue>.Create;
  try
    _Configuration.Add('FileLogFileName', '20160320.Log');
    _Configuration.Add('SMTPEmailHost', 'mail.server.lt');
    _Configuration.Add('POP3Server', 'some_server');

    for _ObjectType := Low(TObjectTypes) to High(TObjectTypes) do
    begin
      _Interf := TTheFactory.Make(_ObjectType, _Configuration);
      if Assigned(_Interf) then
      begin
        OutputDebugString(PWideChar((_Interf as TObject).ClassName));
        if Supports(_Interf, IEmail) then
          (_Interf as IEmail).Send('X');

        if Supports(_Interf, ILogger) then
          (_Interf as ILogger).GetLastErrorMsg;
      end;
    end;
  finally
    FreeAndNil(_Configuration);
  end;
end;

所以,我需要一个通用工厂,并且能够不通过所有 TObjectTypes 循环,而是通过所有 TLoggerTypes 或通过所有 TEmailTypes 并跳过创建一些例如lDatabase 来自 TLoggerTypes 或 etPOP3 来自 TEmailTypes。

工厂应该生产各种类。

【问题讨论】:

  • 我完全不明白这个问题

标签: delphi generics delphi-xe2 factory-pattern


【解决方案1】:

在 Delphi 中制造工厂非常简单,这要归功于元类(类引用),其中的简单示例是 TClass

TClass = class of TObject

在大多数情况下,您应该为所有工厂成员定义自己的抽象类并为其定义元类:

TMyFactoryObject = class (TObject)
  public
    constructor FactoryCreate(aConfiguration: TConfiguration); virtual; abstract;
end;

TMyFactoryClass = class of TMyFactoryObject;

在这个抽象类中,您可以添加一些对所有后代通用的方法,在我的示例中,我们有将配置作为参数的构造函数。如何应对,将由后代决定。

然后你声明后代类:

TMyLogger = class (TMyFactoryObject, ILogger)
  private
    ...
  public
    constructor FactoryCreate(aConfiguration: TConfiguration); override;
    ... //implementation of ILogger interface etc
  end;

TMyEmail = class (TMyFactoryObject, IEmail)
  private
    ...
  public
    constructor FactoryCreate(aConfiguration: TConfiguration); override;
    ... //implementation of IEmail interface etc
  end;

现在您声明了可能的后代类数组:

var 
  MyFactory: array [otLogger..otEmail] of TMyFactoryClass;

并在初始化部分或其他地方填充此数组:

MyFactory[otLogger]:=TMyLogger;
MyFactory[orEmail]:=TMyEmail;

最后,您问题中的TTheFactory.Make(_ObjectType, _Configuration); 可以替换为:

MyFactory[_ObjectType].FactoryCreate(_Configuration);

您将获得所需的对象作为 MyFactoryObject 类型的实例。

更多信息请参见http://docwiki.embarcadero.com/RADStudio/Seattle/en/Class_References

【讨论】:

  • 为什么将构造函数命名为 FactoryCreate 而不是普通的旧 Create?
  • @RobKennedy 我担心如果 OP 从 TComponent 继承它,它会与通常的 TComponent.Create(aOwner) 冲突,这是很可能的。也许很难找到,因为它编译得很好,并不是每个人都注意警告'constructor Create hides virtual method of base type TComponent'
  • 我最终会得到几个不同的工厂。我什么都想要。
  • @EdijsKolesnikovičs 记录器和电子邮件是不同的对象,还是一个对象可以同时支持记录和电子邮件?添加到您的问题中,您的工厂应该生产什么课程?你对待不同的不同对象是如何最终进入一个循环的?
  • 使用这里演示的技术,Edijs,您假设的整体工厂中的所有特定于类的代码都被转移到它所属的各个类中。您的“通用工厂”剩下的唯一动作是决定要实例化哪个特定类,Yuriy 展示了如何通过简单的数组查找来实现这一点。 Delphi 类本质上类工厂。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-28
  • 1970-01-01
  • 2013-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多