Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Сохранение и загрузка данных в объекты на примере коллекций

Если в Вашей программе используются классы для описания объектов некоторой предметной области, то данные, их инициализирующие, можно хранить и в базе данных. Но можно выбрать гораздо более продуктивный подход, который доступен в Delphi/C++ Builder. Среда разработки Delphi/C++ Builder хранит ресурсы всех форм в двоичных или текстовых файлах и эта возможность доступна и для разрабатываемых с ее помощью программ. В данном случае, для оценки удобств такого подхода лучше всего рассмотреть конкретный пример.

Необходимо реализовать хранение информации о некоей службе рассылки и ее подписчиках. Будем хранить данные о почтовом сервере и список подписчиков. Каждая запись о подписчике хранит его личные данные и адрес, а также список тем(или каталогов), на которые он подписан. Как большие поклонники Гради Буча (Grady Booch), а также будучи заинтересованы в удобной организации кода, мы организуем информацию о подписчиках в виде объектов. В Delphi для данной задачи идеально подходит класс TCollection, реализующий всю необходимую функциональность для работы со списками типизированных объектов. Для этого мы наследуемся от TCollection, называя новый класс TMailList, а также создаем наследника от TCollectionItem - TMailClient. Последний будет содержать все необходимые данные о подписчике, а также реализовывать необходимые функции для работы с ним.

Начнем с TMailClient.


type
  TMailClient = class(TCollectionItem)
  private
    FName: string;
    FAddress: string;
    FEnabled: boolean;
    FFolders: TStringList;
  public
    Files: TStringList;
    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, хранящий объекты класса TMailClient, приведен ниже.


  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, содержащий также и данные о параметрах доступа к почтовому серверу для отправки почты.


  TMailer = class(TComponent)
  private
    name: string;
    EMail: string;
    FMailList: TMailList;
    FLastActivated: TDateTime;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property MailList: TMailList read FMailList write FMailList;
    property LastActivated: TDateTime read FLastActivated write FLastActivated;
  end;

В данном случае мы наследуемся от класса TComponent, для того, чтобы была возможности записи данных объекта в файл. Свойство MailList содержит уже объект класса TMailList, а свойство LastActivated - дату последнего запуска программы рассылки.

Реализация всех приведенных классов приведена ниже.


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;

procedure TMailClient.PickFiles;
var
  i: integer;
begin
  for i := 0 to Folders.Count - 1 do
    CreateFileList(Files, Folders[i]);
end;

function TMailList.GetMailClient(index: Integer): TMailClient;
begin
  Result := TMailClient(inherited Items[index]);
end;

procedure TMailList.SetMailClient(index: Integer; Value: TMailClient);
begin
  Items[index].Assign(Value);
end;

function TMailList.Add: TMailClient;
begin
  Result := TMailClient(inherited Add);
end;

constructor TMailer.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  MailList := TMailList.Create(TMailClient);
  FLastActivated := now;
end;

destructor TMailer.Destroy;
begin
  MailList.Free;
  inherited;
end;

Функция CreateFileList создает по каким-либо правилам список файлов на основе переданного ей списка каталогов, обходя их рекурсивно.


procedure CreateFileList(sl: TStringList; 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
      CreateFileList(sl, FilePath + '\' + sr.name);
  end;

begin
  if not DirectoryExists(FilePath) then
    exit;
  if FindFirst(FilePath + '\' + Settings.IncludeFileMasks,
  faAnyFile , sr) = 0 then
    ProcessFile;
  while FindNext(sr) = 0 do
    ProcessFile;
  FindClose(sr);
end;

В итоге мы располагаем классом TMailer, содержащим всю необходимую нам информацию. Теперь перейдем к созданию объекта, их сохранению и загрузке.


var
  sDataFile: string;
  MLN: TMailLateNight;
begin
  MLN := TMailLateNight.Create(nil);
  sDataFile := ExtractFilePath(ParamStr(0)) + 'users.dat';

  //...загрузка данных из файла
  if FileExists(sDatsFile) then
    LoadUsersFromTextFile(MLN, sDatsFile);

  ...

  //...работа с объектами
  for i:=0 to MLN.MailList.Count-1 do
  begin
    s := MLN.MailList[i].name;
    s := MLN.MailList[i].Address;

    MLN.MailList[i].PickFiles;

    for j:=0 to MLN.Files.Count-1 do
    begin
      s := MLN.MailList[i].Files[j];

      ...

      //...сохранение данных в файл
      SaveComponentToTextFile(MLN, sDataFile);

Хранение данных в файле позволяет оказаться от использования БД, если объем данных не слишком велик и нет необходимости в совместном доступе к данным.

Самое главное - мы организуем все данные в виде набора удобных для работы классов и не тратим время на их сохранение и инициализацию из БД.

Далее приведен код функций для сохранения/чтения компонента.


//...процедура удаляет все дочерние компоненты из формы
procedure DeleteComponents(Form: TForm);
var
  i: integer;
begin
  for i := Form.ComponentCount - 1 downto 0 do
    Form.Components[i].Free;
end;

// ...процедура загружает(инициализирует)
// компонент из текстового файла с ресурсом
procedure LoadComponentFromTextFile(Component: TComponent;
FileName: string);
var
  ms: TMemoryStream;
  fs: TFileStream;
begin
  ms := TMemoryStream.Create;
  fs := TFileStream.Create(FileName, fmOpenRead);
  try
    ObjectTextToBinary(fs, ms);
    ms.position := 0;
    ms.ReadComponent(Component);
  finally
    ms.Free;
    fs.free;
  end;
end;

//...процедура сохраняет компонент в текстовый файл
procedure SaveComponentToTextFile(Component: TComponent;
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;

Проект Delphi World © Выпуск 2002 - 2004
Автор проекта: ___Nikolay