2016-06-08 47 views
2

我有多個集合,如:如何在Delphi中設計單個界面以保存不同的集合?

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

TRoot = class 
    value : string 
end; 

TFoo = class(TRoot) 
... 
end; 

TBar = class(TRoot) 
... 
end; 

而且我有一個接口/類,可以保存或加載的集合:

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

注意接口需要一個鍵/字符串對集合順序正常工作。

我實現了一些ISAVE類以加載/收集從文件或數據庫保存到/:

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

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

所以,最後一塊會從每個集合繼承和創建保存/載入方法從TDictionary(字符串,字符串)

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)保存或加載的接口需要集合中的字符串值,並且儘管每個集合中的所有對象都繼承自定義了此字符串的類,但我不知道如何將集合TDictionary<string,TFoo>轉換爲TDictionary<string,string>而無需訴諸上面的代碼(這將複製集合以便將其傳遞給iSave對象)。我覺得雖然我可以替換iSave對象來改變集合保存/加載的方式,而不用改變集合本身,但我不知道是否最好的方法來保存/加載集合相關對象。

回答

2

我認爲你是這樣做的錯誤方式。

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; 
+0

你說得挺有道理的說,每個類(TFoo,TBAR)應該有責任把自己轉變成字符串。 但是你不認爲集合的加載/保存過程有點「面向流」嗎?我認爲編寫數據庫版本(TDbSerialize)會很困難,不是嗎? – MarcosCunhaLima

+0

@MarcosCunhaLima不,我認爲這不會很難。我所展示的只是一個例子。如果需要,您可以將其他方法添加到ISerialize中,以便DB實現可以爲每個集合項目創建一個新記錄,然後Write方法可以根據需要更新記錄列,然後在完成時提交。或者爲要寫入的整個集合創建一個新的blob。無論你需要什麼。 –

+0

你是對的。謝謝 – MarcosCunhaLima