Общий заводской цикл

Я пытаюсь выяснить, как написать общую фабрику в 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.

Фабрика должна производить все виды классов.

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 для получения дополнительной информации.

Другие вопросы по тегам