Общий заводской цикл
Я пытаюсь выяснить, как написать общую фабрику в 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 для получения дополнительной информации.