Reputation: 301
I have multiple collections like:
TFooList = TObjectDictionary<string,TFoo>;
TBarList = TObjectDictionary<string,TBar>;
....
TRoot = class
value : string
end;
TFoo = class(TRoot)
...
end;
TBar = class(TRoot)
...
end;
And I have an interface/class that could save or load collections:
ISave = interface
procedure Save( TDictionary<string, string> );
function Load: TDictionary<string, string>;
end;
Note that the interface expects a key/string pair collection in order to work properly.
I implemented some ISave classes in order to load/save the collection to/from file or databases:
TDbSave = class( TInterfacedObject , ISave )
....
end;
iSave := TDbSave( ConnString )
TFileSave = class( TInterfacedObject , ISave )
....
end;
iSave := TFileSave( fileName );
So, the last piece would be inherit from each collection and create the save/load methods to "translate" each collection into/from 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;
So, I have two problems with this approach:
1) The interface that save or load expects a string value from the Collection and, although all the objects in each collection inherit from a class that has this string defined, I don't know how to transform a collection like TDictionary<string,TFoo>
into a TDictionary<string,string>
without resorting to the code above (which will duplicate the collection in order to pass it to iSave
object).
2) I feel that, although I can replace iSave
objects changing the way the collections would be saved/loaded without changing the collections themselves, I don't know if it is the best approach to save/load collections that keep related objects.
Upvotes: 2
Views: 378
Reputation: 597215
I think you are going about this the wrong way.
ISave
should not have any concept of any TDictionary
at all. It should just expose methods for reading/writing basic data types (integers, strings, etc). Let TFooListSavable
and TBarListSavable
decide how to serialize their respective TDictionary
data however they want, calling the ISave
methods as needed.
Even better would be if TFooListSavable
and TBarListSavable
pass ISave
to each individual TFoo
/TBar
and let them serialize their own data members directly.
For example, something like this:
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;
Upvotes: 2