考虑像
这样的界面IMyInterface = interface
procedure DoSomethingRelevant;
procedure Load (Stream : TStream);
procedure Save (Stream : TStream);
end;
以及几个实现该接口的类:
TImplementingClass1 = class (TInterfacedObject, IMyInterface)
...
end;
TImplementingClass2 = class (TInterfacedObject, IMyInterface)
...
end;
...
我有一个包含IMyInterface实现者列表的类:
TMainClass = class
strict private
FItems : TList <IMyInterface>;
public
procedure LoadFromFile (const FileName : String);
procedure SaveToFile (const FileName : String);
end;
现在问题:如何以面向对象的方式加载主类,尤其是项目列表?在我可以为项目调用虚拟Load方法之前,我必须创建它们,因此必须知道它们的类型。在我目前的实现中,我存储项目数,然后存储每个项目
但这意味着在加载过程中我必须做一些像
这样的事情ID := Reader.ReadInteger;
case ID of
itClass1 : Item := TImplementingClass1.Create;
itClass2 : Item := TImplementingClass2.Create;
...
end;
Item.Load (Stream);
但这似乎并不是面向对象的,因为每次添加新的实现者时我都必须使用现有的代码。有没有更好的方法来处理这种情况?
答案 0 :(得分:4)
一种解决方案是实施一个工厂,所有类都使用唯一的ID注册它们。
TCustomClassFactory = class(TObject)
public
procedure Register(AClass: TClass; ID: Integer);
function Create(const ID: Integer): IMyInterface;
end;
TProductionClassFactory = class(TCustomClassFactory)
public
constructor Create; override;
end;
TTestcase1ClassFactory = class(TCustomClassFactory);
public
constructor Create; override;
end;
var
//***** Set to TProductionClassFactory for you production code,
// TTestcaseXFactory for testcases or pass a factory to your loader object.
GlobalClassFactory: TCustomClassFactory;
implementation
constructor TProductionClassFactory.Create;
begin
inherited Create;
Register(TMyImplementingClass1, 1);
Register(TMyImplementingClass2, 2);
end;
constructor TTestcase1ClassFactory.Create;
begin
inherited Create;
Register(TMyImplementingClass1, 1);
Register(TDoesNotImplementIMyInterface, 2);
Register(TDuplicateID, 1);
Register(TGap, 4);
...
end;
<强>优点强>
答案 1 :(得分:2)
您需要一个类注册表,您可以在其中存储每个类引用及其唯一ID。这些类在其单元的初始化部分注册。
TImplementingClass1 = class (TInterfacedObject, IMyInterface)
...
end;
TImplementingClass2 = class (TInterfacedObject, IMyInterface)
...
end;
TMainClass = class
public
procedure LoadFromFile (const FileName : String);
procedure SaveToFile (const FileName : String);
end;
编辑:将类注册表移动到一个单独的类中:
TMyInterfaceContainer = class
strict private
class var
FItems : TList <IMyInterface>;
FIDs: TList<Integer>;
public
class procedure RegisterClass(TClass, Integer);
class function GetMyInterface(ID: Integer): IMyInterface;
end;
procedure TMainClass.LoadFromFile (const FileName : String);
...
ID := Reader.ReadInteger;
// case ID of
// itClass1 : Item := TImplementingClass1.Create;
// itClass2 : Item := TImplementingClass2.Create;
// ...
// end;
Item := TMyInterfaceContainer.GetMyInterface(ID);
Item.Load (Stream);
...
initialization
TMyInterfaceContainer.RegisterClass(TImplementingClass1, itClass1);
TMyInterfaceContainer.RegisterClass(TImplementingClass2, itClass2);
这应该指向你的方向,对这些方法的一个非常好的介绍阅读着名的Martin Fowler article,尤其是。关于Interface Injection
的部分