问题
I am trying to figure out how to write a generic factory in XE2. Lets say I have this:
type
TObjectTypes = (otLogger, otEmail);
type
TLoggerTypes = (lFile, lConsole, lDatabase);
type
TEmailTypes = (etPOP3, etSMTP);
Classes:
TSMTPEmail = class(TInterfacedObject, IEmail); // Supports emailing only
TPOP3Email = class(TInterfacedObject, IEmail); // Supports emailing only
TFileLogger = class(TInterfacedObject, ILogger); // Supports logging only
etc.
Now I do this to loop thru all 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;
So, I need a generic factory and be able to loop not thru all TObjectTypes, but thru all TLoggerTypes or thru all TEmailTypes and skip creating some e.g. lDatabase from TLoggerTypes or etPOP3 from TEmailTypes.
Factory should produce all kind of classes.
回答1:
In Delphi making factories is pretty simple, thanks to metaclasses (class references), simple example of which is TClass
:
TClass = class of TObject
In most cases, you should define your own abstract class for all factory members and metaclass for it:
TMyFactoryObject = class (TObject)
public
constructor FactoryCreate(aConfiguration: TConfiguration); virtual; abstract;
end;
TMyFactoryClass = class of TMyFactoryObject;
In this abstract class you can add some methods common for all descendants, in my example we have constructor which takes configuration as argument. How to react to it will be determined in descendants.
Then you declare descendant classes:
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;
now you declare array of possible descendant classes:
var
MyFactory: array [otLogger..otEmail] of TMyFactoryClass;
and in initialization section or in other places you populate this array:
MyFactory[otLogger]:=TMyLogger;
MyFactory[orEmail]:=TMyEmail;
At last, TTheFactory.Make(_ObjectType, _Configuration);
from your question can be replaced with:
MyFactory[_ObjectType].FactoryCreate(_Configuration);
and you'll get needed object as instance of type MyFactoryObject.
See http://docwiki.embarcadero.com/RADStudio/Seattle/en/Class_References for more information.
来源:https://stackoverflow.com/questions/36111172/generic-factory-looping