Возникает ситуация, когда необходимо сохранить и загрузить различные коллекции в формате TDictionary<string, string> через интерфейс ISave в Delphi. В данной статье мы рассмотрим, как это можно сделать, а также обсудим альтернативный подход к решению этой задачи.
Проблема
У нас есть несколько коллекций, каждая из которых представляет собой словарь с ключами в виде строк и значениями в виде пользовательских типов:
Теперь нам нужно как-то "перевести" наши коллекции в формат TDictionary<string, string> и обратно, чтобы использовать их с интерфейсом ISave.
Подход 1: Наследование и переопределение методов
Один из подходов - наследовать каждый тип коллекции и добавить методы Save и Load, которые будут "переводить" коллекцию в нужный формат и обратно. Например:
TFooListSavable = TFooList;
procedure Create(save_load: ISave);
procedure Save;
procedure Load;
...
end;
procedure TFooListSavable.Save
// 1. создать TDictionary<string, string>
// 2. загрузить этот словарь из нашей коллекции, переводя каждый объект Foo в строку
// 3. вызвать save_load.Save(dictionary);
end;
procedure TFooListSavable.Load
// 1. создать TDictionary<string, string>
// 2. вызвать save_load.Load(), чтобы загрузить его
// 3. пройтись по коллекции и перевести строки в объекты TFoo
// 4. добавить каждый созданный объект TFoo в TFooListSavable.
end;
Однако у этого подхода есть две проблемы:
Интерфейс ISave ожидает в качестве параметра словарь с ключами и значениями в виде строк. Несмотря на то, что все объекты в коллекциях наследуются от класса TRoot, в котором определена строка, мы не знаем, как преобразовать коллекцию типа TDictionary<string, TFoo> в TDictionary<string, string> без дублирования коллекции, чтобы передать ее объекту iSave.
Хотя мы можем Replace iSave объекты, меняя способ сохранения/загрузки коллекций без изменения самих коллекций, мы не уверены, является ли этот подход лучшим для сохранения/загрузки коллекций, содержащих связанные объекты.
Альтернативный ответ: Переопределение методов в самих объектах коллекции
Вместо того чтобы наследовать каждый тип коллекции и добавлять методы Save и Load, мы можем переопределить эти методы непосредственно в самих объектах коллекции. Для этого нам понадобится интерфейс ISerialize, который будет определять методы для чтения/записи базовых типов данных (целых чисел, строк и т.д.). Каждый объект коллекции сможет самостоятельно решать, как сериализовать свои данные, вызывая методы интерфейса ISerialize по мере необходимости.
Давайте рассмотрим пример, как это можно реализовать:
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;
В данном примере мы определили интерфейс ISerialize, который позволяет сериализовать и десериализовать объекты. Каждый объект коллекции может самостоятельно решать, как сериализовать свои данные, вызывая методы интерфейса ISerialize. Это позволяет нам сохранить и загрузить коллекции в формате TDictionary<string, string> через интерфейс ISave в Delphi.
Заключение
В данной статье мы рассмотрели проблему сохранения и загрузки различных коллекций в формате TDictionary<string, string> через интерфейс ISave в Delphi. Мы обсудили два подхода к решению этой задачи: наследование и переопределение методов в классах коллекций и переопределение методов непосредственно в самих объектах коллекции. Второй подход основан на использовании интерфейса ISerialize, который позволяет сериализовать и десериализовать объекты. Это позволяет нам сохранить и загрузить коллекции в нужном формате через интерфейс ISave в Delphi.
В данной статье рассматривается проблема сохранения и загрузки различных коллекций в формате `TDictionary` через интерфейс `ISave` в Delphi, а также обсуждаются два подхода к решению этой задачи: наследование и переопределение методов в кл
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.