Прочитав кучу статей о том, как велико ООП и как здорово, что оно есть, решил что пора пользоваться этим всеми прелестями, которое оно нам несёт. Прежде чем изобретать велосипед внимательно были искурены форумы, которые содержали в себе слова: сериализация, XML и JSON. Всё было прекрасно, хорошо документировано, но ощущение чего то незаконченного не покидало…
Были пару робких попыток пристроить для своих целей XML или JSON, но сталкивался с тем что над реализацией хранения данных в этих форматах, в каждом новом проекте, уходило не мало времени на написание методов сохранения и загрузки. Всё таки не даёт XML (и ему подобные) того уровня абстракции который бы хотелось иметь в наличии…
Пока вёл свои изыскания с XML неоднократно наталкивался на практику применения RTTI в делфи. Но так и не прижилось… Разведения гетеров и сетеров для published свойств сильно засоряло код, хотя свои плюсы несомненно есть в этом подходе. Но смысл для не очень сложного проекта, с простой системой классов разводить весь этот огород? В дальнейшем я наткнулся на коллекции и в принципе они хорошо справляются с отведёнными для них задачами. Но… Не хватает универсальности использования и монотонность одних и тех же действий в разных проектах при работе с ними меня лично напрягало. Всё это в итоге сподвигло меня на написания своего велосипеда…
Итак, что я хотел или сама идея: есть некий сферический класс в вакууме, который имеет некоторые свойства и, соответственно, значения этих свойств:

Так же было бы здорово. Если бы был класс, который хранил множество «сферических» классов:

А уже вообще счастью не было бы предела, если бы создать такой класс, который мог бы сохранять класс-хранитель в файл, да ещё в разных форматах… Вот такая получилась сказка.
Собственно, как это всё сделать и заставить работать. Итак, посмотрим на наш сферический класс.
Были пару робких попыток пристроить для своих целей XML или JSON, но сталкивался с тем что над реализацией хранения данных в этих форматах, в каждом новом проекте, уходило не мало времени на написание методов сохранения и загрузки. Всё таки не даёт XML (и ему подобные) того уровня абстракции который бы хотелось иметь в наличии…
1. Истина где-то рядом
Пока вёл свои изыскания с XML неоднократно наталкивался на практику применения RTTI в делфи. Но так и не прижилось… Разведения гетеров и сетеров для published свойств сильно засоряло код, хотя свои плюсы несомненно есть в этом подходе. Но смысл для не очень сложного проекта, с простой системой классов разводить весь этот огород? В дальнейшем я наткнулся на коллекции и в принципе они хорошо справляются с отведёнными для них задачами. Но… Не хватает универсальности использования и монотонность одних и тех же действий в разных проектах при работе с ними меня лично напрягало. Всё это в итоге сподвигло меня на написания своего велосипеда…
Итак, что я хотел или сама идея: есть некий сферический класс в вакууме, который имеет некоторые свойства и, соответственно, значения этих свойств:

Так же было бы здорово. Если бы был класс, который хранил множество «сферических» классов:

А уже вообще счастью не было бы предела, если бы создать такой класс, который мог бы сохранять класс-хранитель в файл, да ещё в разных форматах… Вот такая получилась сказка.
2. Укрощение, воплощение, мечты и реальность
Собственно, как это всё сделать и заставить работать. Итак, посмотрим на наш сферический класс.
- TClassItem = class(TCollectionItem)
- private
- FName:String;
- FProperty: TStringList;
- FStorage: TClassItems;
- FPrt:Integer;
- public
- ParentItem: TClassItem;
- constructor Create(Collection: TCollection); override;
- function GetPropAsHTML: String;
- function GetAddress(separator:char): String;
- destructor Destroy; override;
- published
- property Name:String read FName write FName;
- property Parent: Integer read FPrt write FPrt;
- property Prop: TStringList read FProperty write FProperty;
- property Storage: TClassItems read FStorage write FStorage;
- end;
* This source code was highlighted with Source Code Highlighter.
Видим, что есть имя, есть родитель, стринглист со парами «свойство-значение» и хранилище себе подобных. Рассмотрим хранилище себе подобных поближе. Это как раз тот «класс-хранитель», что был выше.
- TClassItems = class(TCollection)
- Private
- ParentItem:TClassItem;
- function GetItem(Index: Integer): TClassItem;
- procedure SetItem(Index: Integer; Value: TClassItem);
- public
- function AddOrGet(Name:String): TClassItem;
- function ItemExists(Name:String):Boolean;
- property Items[Index: Integer]: TClassItem read GetItem write SetItem;
- end;
* This source code was highlighted with Source Code Highlighter.
Думаю тут всё более-менее понятно. Далее посмотрим на реализацию класса, который обеспечивал бы хранение в файлах.
- TSaveClass = class(TComponent)
- private
- FStorage: TClassItems;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
-
- Procedure SaveToFile(FileName:String);
- Procedure LoadFromFile(FileName:String);
-
- procedure LoadFromTextFile(FileName: String);
- procedure SaveToTextFile(FileName: String);
-
- Procedure SaveToCompressFile(FileName:String);
- Procedure LoadFromCompressFile(FileName:String);
-
- Function GetItemFromAddress(Adr:String;separator:char):TClassItem;
-
- published
- property Storage: TClassItems read FStorage write FStorage;
- end;
* This source code was highlighted with Source Code Highlighter.
Не сложно заметить, что работает с тремя типами файлов — бинарные, текстовые и (на десерт) со сжатием. Ну и конечно пример работы со всем этим счастьем:
- procedure TForm1.Button1Click(Sender: TObject);
- var
- Shkaf,Sections,Section,Polka:TClassItem;
- begin
- sc:=TSaveClass.Create(nil);
- // Создание
- Shkaf:=sc.Storage.AddOrGet('Shkaf');
- // Задание значений свойств
- Shkaf.Prop.Values['W']:='3000';
- Shkaf.Prop.Values['H']:='1800';
- Shkaf.Prop.Values['D']:='300';
- // Создание дочернего объекта
- Sections:= Shkaf.Storage.AddOrGet('Sections');
- Section:= Sections.Storage.AddOrGet('Section1');
- Section.Prop.Values['width']:='152';
- Polka:=Section.Storage.AddOrGet('Polka1');
- Polka.Prop.Values['Position']:='450';
- Polka:=Section.Storage.AddOrGet('Polka2');
- Polka.Prop.Values['Position']:='0';
- Section:= Sections.Storage.AddOrGet('Section2');
- Section.Prop.Values['width']:='300';
- // Сохраняем в текстовый файл
- sc.SaveToTextFile('sc.txt');
- // Сохраняем в бинарный файл
- sc.SaveToFile('sc.bin');
- // Сохраняем в Zip-файл
- sc.SaveToCompressFile('sc.z');
- //Уничтожаем объект
- sc.Free;
-
- //Создаём заново
- sc:=TSaveClass.Create(Form1);
- //Загружаем из Zip-файла
- sc.LoadFromCompressFile('sc.z');
- //Получаем объект
- Shkaf:=sc.Storage.AddOrGet('Shkaf');
- //Отображаем его в дереве вместе со всеми потомками
- ToTreeViev(Shkaf,Nil);
- end;
* This source code was highlighted with Source Code Highlighter.
В данном случае мне надо было сохранить довольно сложную структуру «Шкаф-купе», в котором произвольное количество секций, в каждой секции — полки, ящики и прочие радости быта домохозяек. Тут конечно пример создания руками, в настоящем проекте, это всё делается автоматом и на лету.

3. Объективная реальность
Собственно предложенное решение не претендует на шедевр, но согласитесь создание приложений существенно ускорится. Да, я знаю что TstringList не надо было использовать и RTTI не есть “светлый путь познания Дзен”, но всё же я этим пользуюсь по той простой причине, что быстро и главное я оперирую объектами напрямую и использую один и тот же код в разных проектах.
Исходники прилагаются: