面向对象和序列化

时间:2009-08-05 06:51:15

标签: delphi serialization oop delphi-2009

考虑像

这样的界面
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方法之前,我必须创建它们,因此必须知道它们的类型。在我目前的实现中,我存储项目数,然后存储每个项目

  • 类型标识符(IMyInterface获取额外的GetID函数)
  • 调用项目的Save方法

但这意味着在加载过程中我必须做一些像

这样的事情
ID := Reader.ReadInteger;
case ID of
  itClass1 : Item := TImplementingClass1.Create;
  itClass2 : Item := TImplementingClass2.Create;
  ...
end;
Item.Load (Stream);

但这似乎并不是面向对象的,因为每次添加新的实现者时我都必须使用现有的代码。有没有更好的方法来处理这种情况?

2 个答案:

答案 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;

<强>优点

  • 您可以从当前的加载方法中删除条件逻辑。
  • 检查重复或遗失ID的地方。

答案 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

的部分