delphi - 如何在Delphi中设计一个单一的界面来保存不同的Collections?

标签 delphi collections interface

我有多个集合,例如:

TFooList = TObjectDictionary<string,TFoo>;
TBarList = TObjectDictionary<string,TBar>;
....

TRoot = class
   value : string
end;

TFoo = class(TRoot)
...
end;

TBar = class(TRoot)
...
end;

我有一个可以保存或加载集合的接口(interface)/类:

ISave = interface
  procedure Save( TDictionary<string, string> );
  function Load: TDictionary<string, string>;
end;

请注意,该界面需要 key /字符串对集合才能正常工作。

我实现了一些 ISave 类,以便将集合加载到文件或数据库或从文件或数据库保存集合:

TDbSave = class( TInterfacedObject , ISave )
   ....
end; 
iSave := TDbSave( ConnString )

TFileSave = class( TInterfacedObject , ISave )
   ....
end; 
iSave := TFileSave( fileName );

因此,最后一部分将从每个集合继承,并创建保存/加载方法以将每个集合“转换”为 TDictionary(string, string) 或从 TDictionary(string, string)“转换”

    TFooListSavable = TFooList;
      procedure Create( save_load : ISave );
      procedure Save;
      procedure Load;
      ....
   end;

   procedure TFooListSavable.Save
      // 1. create a  TDictionary<string, string>
      // 2. load the dictionary above with my collection translating
      //    each Foo object into a string
      // 3. call save_load.Save( dictionary );
   end;    
   procedure TFooListSavable.Load
      // 1. create a  TDictionary<string, string>
      // 2. call save_load.load to load it 
      // 3. Move over the collection and translate string into TFoo and
      // 4. AddOrEquals each TFoo created into TFooListSavable.
   end; 

所以,我对这种方法有两个问题:

1) 保存或加载的接口(interface)需要来自 Collection 的字符串值,尽管每个集合中的所有对象都继承自定义了此字符串的类,但我不知道如何将像 TDictionary<string,TFoo> 这样的集合转换为a TDictionary<string,string>,而不使用上面的代码(这将复制集合以便将其传递给 iSave 对象)。

2)我觉得,虽然我可以替换 iSave 对象来更改集合的保存/加载方式,而无需更改集合本身,但我不知道这是否是保存/加载保持相关的集合的最佳方法对象。

最佳答案

我认为您的处理方式是错误的。

ISave 根本不应该有任何 TDictionary 的概念。它应该只公开读取/写入基本数据类型(整数、字符串等)的方法。让 TFooListSavableTBarListSavable 决定如何序列化各自的 TDictionary 数据,根据需要调用 ISave 方法.

如果TFooListSavableTBarListSavableISave传递给每个单独的TFoo/TBar,那就更好了并让他们直接序列化自己的数据成员。

例如,像这样:

type
  ISerialize = interface
    function HasData: Boolean;
    procedure StartWriteCollection;
    procedure StartWriteItem;
    procedure FinishWriteCollection;
    procedure FInishWriteItem;
    procedure WriteBoolean(value: Boolean);
    procedure WriteInteger(value: Integer);
    procedure WriteString(const value: String);
    ...
    procedure StartReadCollection;
    procedure StartReadItem;
    procedure FinishReadCollection;
    procedure FinishReadItem;
    function ReadBoolean: Boolean;
    function ReadInteger: Integer;
    function ReadString: String;
    ...
  end;

  TRoot = class
  public
    value : string;
    constructor Create; virtual;
    procedure Save(Dest: ISerialize); virtual;
    procedure Load(Src: ISerialize); virtual;
  end;

  TBaseList<T: TRoot, constructor> = class(TObjectDictionary<string, T>)
  public
    procedure Save(Dest: ISerialize);
    procedure Load(Src: ISerialize);
  end;

  TFoo = class(TRoot)
  public
    myint: Integer;
    ...
    procedure Save(Dest: ISerialize); override;
    procedure Load(Src: ISerialize); override;
  end;
  TFooList = TBaseList<TFoo>;

  TBar = class(TRoot)
    mybool: Boolean;
    ...
    procedure Save(Dest: ISerialize); override;
    procedure Load(Src: ISerialize); override;
  end;
  TBarList = TBaseList<TBar>;

  TDbSerialize = class(TInterfacedObject, ISerialize)
    ...
  end; 

  TFileSerialize = class(TInterfacedObject, ISerialize)
    ...
  end; 

  procedure TBaseList<T>.Save(Dest: ISerialize);
  var
    pair: TPair<string, T>;
  begin
    Dest.StartWriteCollection;
    for pair in Self do
    begin
      Dest.StartWriteItem;
      Dest.WriteString(pair.Key);
      TRoot(pair.Value).Save(Dest);
      Dest.FinishWriteItem;
    end;
    Dest.FinishWriteCollection;
  end;

  procedure TBaseList<T>.Load(Src: ISerialize);
  var
    Cnt, I: Integer;
    key: string;
    value: T;
  begin
    Self.Clear;
    Src.StartReadCollection;
    While Src.HasData do
    begin
      Src.StartReadItem;
      key := Src.ReadString;
      value := T.Create;
      try
        value.Load(Src);
        Self.Add(key, value);
      except
        value.Free;
        raise;
      end;
      Src.FinishReadItem;
    end;
    Src.FinishReadCollection;
  end;

  procedure TRoot.Save(Dest: ISerialize);
  begin
    Dest.WriteString(value);
  end;

  procedure TRoot.Load(Src: ISerialize);
  begin
    value := Src.ReadString;
  end;

  procedure TFoo.Save(Dest: ISerialize);
  begin
    inherited;
    Dest.WriteInteger(myint);
  end;

  procedure TFoo.Load(Src: ISerialize);
  begin
    inherited;
    myint := Src.ReadInteger;
  end;

  procedure TBar.Save(Dest: ISerialize);
  begin
    inherited;
    Dest.WriteBoolean(mybool);
  end;

  procedure TBar.Load(Src: ISerialize);
  begin
    inherited;
    mybool := Src.ReadBoolean;
  end;

关于delphi - 如何在Delphi中设计一个单一的界面来保存不同的Collections?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37713575/

相关文章:

java - 查找集合中具有给定属性的所有对象

java - 为什么我的 ArrayList 项会转换为 Java 中的对象?

java - 使用 java8 从两个 java 列表中获取唯一的用户定义对象

php - 缓存 API/接口(interface)以用作模型

c++ - 内部库类型是否应该通过我的库界面可见?

typescript - 根据映射键从接口(interface)映射中选择类型

sql-server - ADOQuery.Locate 慢,创建索引

Delphi TLB编辑器替换

Delphi以不同用户身份运行

delphi - Indy10 - TIdContext 到 TIdTcpServer