скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

Сохранение и загрузка данных в объекты на примере коллекций



Если в Вашей программе используются классы для описания объектов некоторой предметной области, то данные, их инициализирующие, можно хранить и в базе данных. Но можно выбрать гораздо более продуктивный подход, который доступен в 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;






Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook