Сохранение и загрузка данных в объекты на примере коллекций
Если в Вашей программе используются классы для описания объектов некоторой предметной области, то данные, их инициализирующие, можно хранить и в базе данных. Но можно выбрать гораздо более продуктивный подход, который доступен в Delphi/C++ Builder. Среда разработки Delphi/C++ Builder хранит ресурсы всех форм в двоичных или текстовых файлах и эта возможность доступна и для разрабатываемых с ее помощью программ. В данном случае, для оценки удобств такого подхода лучше всего рассмотреть конкретный пример. Необходимо реализовать хранение информации о некоей службе рассылки и ее подписчиках. Будем хранить данные о почтовом сервере и список подписчиков. Каждая запись о подписчике хранит его личные данные и адрес, а также список тем(или каталогов), на которые он подписан. Как большие поклонники Гради Буча (Grady Booch), а также будучи заинтересованы в удобной организации кода, мы организуем информацию о подписчиках в виде объектов. В Delphi для данной задачи идеально подходит класс TCollection, реализующий всю необходимую функциональность для работы со списками типизированных объектов. Для этого мы наследуемся от TCollection, называя новый класс TMailList - список рассылки, а также создаем наследника от TCollectionItem - TMailClient - адресат рассылки. Последний будет содержать все необходимые данные о подписчике, а также реализовывать необходимые функции для работы с ним. Саму коллекцию с подписчиками нам нужно будет поместить в некий базовый класс, который мы и будем сохранять и загружать. На роль такового подходит класс TMailer - почтовый клиент. type TMailClient = class(TCollectionItem) private FName: string; FAddress: string; FEnabled: boolean; FFolders: TStringList; public Files: TStringList; // список файлов к рассылке. заполняется в run-time. Сохранению не подлежит constructor Create(Collection: TCollection); override; destructor Destroy; override; procedure PickFiles; published property Name: string read FName write FName; // имя адресата property Address: string read FAddress write FAddress; // почтовый адрес property Enabled: boolean read FEnabled write FEnabled default true; property Folders: TStringList read FFolders write FFolders; // список папок (тем) подписки end; Класс содержит сведения о имени клиента, его адресе, его статусе(Enabled), а также список каталогов, на которые он подписан. Процедура PickFiles составляет список файлов к отправке и сохраняет его в свойстве Files TMailList = class(TCollection) public function GetMailClient(Index: Integer): TMailClient; procedure SetMailClient(Index: Integer; Value: TMailClient); public function Add: TMailClient; property Items[Index: Integer]: TMailClient read GetMailClient write SetMailClient; default; end; Теперь поместим класс TMailList в класс TMailer. В него можно будет потом включить данные о параметрах доступа к почтовому серверу для отправки почты. Он мог бы и отправлять почту, но в данном примере это не использовано, дабы не перегружать код. То есть в нашем примере он выполняет только роль носителя данных о подписчиках и их подписке. Класс TComponent, от которого он наследуется можно сохранить в файл, в то время как TCollection самостоятельно не сохранится. Только если она агрегирована в TComponent. Именно это у нас и реализовано. TMailer = class(TComponent) private FMailList: TMailList; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property MailList: TMailList read FMailList write FMailList; // коллекция - список рассылки. // здесь можно поместить, к примеру, данные о соединении с почтовым сервером end; Повторюсь. В данном случае мы наследуемся от класса TComponent, для того, чтобы была возможности записи данных объекта в файл. Свойство MailList содержит уже объект класса TMailList. constructor TMailClient.Create(Collection: TCollection); begin inherited; Folders := TStringList.Create; Files := TStringList.Create; FEnabled := true; end; destructor TMailClient.Destroy; begin Folders.Free; Files.Free; inherited; end; Функция CreateFileList создает по каким-либо правилам список файлов на основе переданного ей списка каталогов, обходя их рекурсивно. К примеру, она может быть реализована так. procedure CreateFileList(sl: TStringList; const FilePath: string); var sr: TSearchRec; procedure ProcessFile; begin if (sr.Name = '.')or(sr.Name = '..') then exit; if sr.Attr <> faDirectory then sl.Add(FilePath + '\' + sr.Name); if sr.Attr = faDirectory then begin CreateFileList(sl, FilePath + '\' + sr.Name); end; end; begin if not DirectoryExists(FilePath) then exit; if FindFirst(FilePath + '\' + '*.*', faAnyFile , sr) = 0 then ProcessFile; while FindNext(sr) = 0 do ProcessFile; FindClose(sr); end; В итоге мы располагаем классом TMailer, содержащим всю необходимую нам информацию. Теперь перейдем к созданию объекта, их сохранению и загрузке. var Mailer: TMailer; // это наш объект для хранения данных о почтовой рассылки // Процедура загрузки данных в объект. Может быть процедурой OnCreate() главной формы. procedure TfMain.FormCreate(Sender: TObject); var sDataFile, sTmp: string; i, j: integer; begin Mailer := TMailer.Create(self); // будем считать, что данные были сохранены в файл users.dat в каталоге программы sDataFile := ExtractFilePath(ParamStr(0)) + 'users.dat'; //...загрузка данных из файла if FileExists(sDataFile) then LoadComponentFromTextFile(Mailer, sDataFile); { здесь данные из файла загружены } После загрузки данных мы можем работать с данными в нашей коллекции подписчиков. Добавлять и удалять их ( Mailer.MailList.Add; Mailer.MailList.Delete(Index); ). При завершении работы программы необходимо сохранить уже новые данные в тот же файл. // Процедура сохранения данных из объекта в файл. Может быть процедурой OnDestroy() главной формы. procedure TfMain.OnDestroy; begin //...сохранение данных в файл users.dat SaveComponentToTextFile(Mailer, ExtractFilePath(ParamStr(0)) + 'users.dat'); end; Хранение данных в файле позволяет оказаться от использования БД, если объем данных не слишком велик и нет необходимости в совместном доступе к данным. Далее приведен код функций для сохранения/чтения компонента. //...процедура загружает(инициализирует) компонент из текстового файла с ресурсом procedure LoadComponentFromTextFile(Component: TComponent; const FileName: string); var ms: TMemoryStream; fs: TFileStream; begin fs := TFileStream.Create(FileName, fmOpenRead); ms := TMemoryStream.Create; try ObjectTextToBinary(fs, ms); ms.position := 0; ms.ReadComponent(Component); finally ms.Free; fs.free; end; end; //...процедура сохраняет компонент в текстовый файл procedure SaveComponentToTextFile(Component: TComponent; const FileName: string); var ms: TMemoryStream; fs: TFileStream; begin fs := TFileStream.Create(FileName, fmCreate or fmOpenWrite); ms := TMemoryStream.Create; try ms.WriteComponent(Component); ms.position := 0; ObjectBinaryToText(ms, fs); finally ms.Free; fs.free; end; end;
Страница сайта http://silicontaiga.ru
Оригинал находится по адресу http://silicontaiga.ru/home.asp?artId=5763 |