TURBO PASCAL |
|
Новости
|
ООП - это очень просто
Выпуск № 6 Выдавать глобальные идеи - это удовольствие. Модуль uRec.pasЗдесь описывается хранимая единица TOne. Для удобства редактирования информации она "выведена" в отдельную запись TInfo. Обратите внимание, что для каждой строки обязательно нужно задать ее длину. Иначе длина строки будет равна 255 символам, а где Вы видели такую фамилию? То есть, если не задать длину строк, то только 5-10 % базы данных будет содержать что-то полезное. Длина строчек задается константами, что облегчает изменение. Здесь же определена еще одна константа с идентификатором версии, но не программы, а структуры полей. Она нужна вот для чего: в дальнейшем я собираюсь хранить информацию в файле. Доступ к этому файлу собираюсь организовать самый быстрый (но пока без потоков, через типизированный файл), а для этого нужно, чтобы каждая единица хранимой информации имела фиксированную длину. Теперь, допустим, сохранили файл MyDB.db при одной структуре записи. Через неделю решили, что надо ввести поле с номером телефона, или просто изменили длину поля для хранения фамилии. Вы легко изменяете структуру записи TInfo. (Я позднее покажу "как", если сами не сообразите) и открываете файл. Длина одной единицы информации изменилась. Теперь, если будете читать "старый" файл, сохраненный по старым стандартам, то получите белиберду. Будет удобно, если программа предупредит Вас о несовпадении версий и предложит сначала запустить другую программу для конвертирования ее структуры. То есть, эта программа прочитает файл по старому стандарту, и сохранит по новому. И вот для реализации этого первая запись в файле содержит номер варианта. При открытии файла нужно будет только сравнить первые символы в файле к константой. Да! И внеся изменения - измените константу с номером версии! Теперь немного слов о том, как будем хранить. Данные я буду располагать в динамической памяти компьютера. И помещать их туда буду оператором New. Обращение к таким данным происходит по их адресу. В "объекте-хранителе" (TBaseObj) я буду хранить адрес первой структуры. В поле Next буду записывать адрес следующей такой структуры или константу NIL, если эта структура последняя и следующей нет. Получается цепочка, по которой можно "проходить" только в одном направлении от начала к концу. Отсюда и название "односвязный список". О таких списках написано уже много и в учебниках и в статьях. (Если Вы не разберетесь в них, напишите - поясню дполнительно) В модуле uRec.pas описаны еще две подпрограммы, которые облегчат будущую работу.
В этом же модуле находится и "хитрая" функция HeapFunc. Дело в том, что Паскаль, если не хватает места для размещения чего-либо в динамической памяти компьютера, реагирует на это "жестко" - аварийно завершает работу программы! Это "не есть удобно" - лучше бы дать "знать" о проблеме программе, а она (мы так должны написать ее, программу эту самую!) пусть посмотрит, может что-то уже можно "выгрузить" из динамической памяти или, по крайней мере, пусть корректно завершит работу. Разработчики заложили в компилятор Borland Pascal такую возможность. У "них"есть функция HeapFunc, которая вызывается, когда есть проблемы с памятью. Программа "смотрит", что возвращает эта функция. Если 0 (ноль), то аварийно завершается. Если функция вернет 2 - это значит, что все прошло хорошо (!?). Неудача была вызвана временными причинами и нужно сделать вторую попытку. Если возвращается значение 1, то это сигнал о том, что памяти нет, но и работу программы завершать не надо. В указатель переменной в этом случае записывается значение NIL. Вот мы и делаем "насильственное" действие. Разработчиками предусмотрено, что программа "узнает" о нахождении такой "Хиповой" функции из переменной процедурного типа HeapError. В исполняемом блоке модуля в эту переменную записывается адрес "нашей" функции. (Видите, все продуманно в Паскале!) unit uRec;
interface
CONST
cNameLen = 12 ;
cForeNameLen = 12;
cVersion : String[cNameLen] = 'Ver.1.0 21.11.03';
TYPE
{Содержательная часть хранимой единицы}
TInfo = record
Name : String[cNameLen];
ForeName: String[cForeNameLen];
Age : Byte;
end;
{Хранимая единица}
POne = ^TOne;
TOne = record
Info : TInfo;
Next : POne;
end;
function NewOne(AName: String; AForeName: String; AAge: Byte): POne;
procedure PrintFullInfo(AOne: Pone);
implementation
{Такая функция облегчит работу}
function NewOne(AName: String; AForeName: String; AAge: Byte): POne;
var
p: POne;
begin
New(p);
with p^ do begin
Next := nil;
Info.Name :=Copy(AName , 1, cNameLen);
Info.ForeName:=Copy(AForeName, 1, cForeNameLen);
Info.Age:=AAge
end;
NewOne:=p
end;
procedure PrintFullInfo(AOne: POne);
begin
if AOne = nil then Exit;
With AOne^.Info do
WriteLn('Фамилия: ',Name,'. Имя: ', ForeName,'. Возраст: ',Age);
end;
{Эта функция нужна для того, чтобы при возникновении ошибок программа
не завершалась аварийно}
function HeapFunc(Size: Word): Integer; far;
begin
HeapFunc:=1
end;
BEGIN
{Настройка функции}
HeapError:=@HeapFunc;
END.
Если что-то осталось непонятным, то пишите - поясню BaseObj.pas
Основную работу совершает объект, который описан в модуле, текст которого показан ниже. В разделе
TYPE описывается только структура объекта. Перед этим (это исключение из общего правила для Паскаля), определяется
указатель на объект
unit BaseObj;
interface
uses
uRec;
TYPE
PBaseObj = ^TBaseObj;
TBaseObj = object
constructor Init;
destructor Done; virtual;
procedure Add(ANewOne: POne); virtual;
procedure PrintAll; virtual;
private
Node: POne;
end;
implementation
constructor TBaseObj.Init;
begin
{Первая запись всегда - информация о версии}
Node:=NewOne(cVersion, cVersion, 0);
end;
procedure TBaseObj.Add(ANewOne: POne);
var p: POne;
begin
if ANewOne = nil then Exit;
{Находим последнюю запись}
p:=Node;
while p^.Next <> nil do
p:=p^.Next;
p^.Next:=ANewOne;
p^.Next^.Next := nil
end;
procedure TBaseObj.PrintAll;
var p: POne;
begin
p:=Node;
while p <> nil do
begin
if Copy(p^.Info.Name,1,3) <> 'Ver' then PrintFullInfo(p);
p:=p^.Next
end
end;
destructor TBaseObj.Done;
var p: POne;
begin
While Node <> nil do
begin
p:=Node^.Next;
Dispose(Node);
Node:=p
end
end;
BEGIN
END.
Описание методов объекта:
Если не ясно что осталось, то напишите. А я только заканчиваю описание главной тестирующей программы и отсылаю. И так уже затянул бессовестно
Test.pasЭто и есть "программа". Она только вызывает объект, кое-что в него заносит, кое-что выводит. В общем, рутинная работа ... program Test;
uses BaseObj, uRec;
var
BO: PBaseObj;
BEGIN
Bo:=New(PBaseObj, Init);
if BO <> nil then
with BO^ do
begin
Add(NewOne('Surin','Борис',12));
PrintAll;
Dispose(BO, Done);
end;
END.
Здесь важно заметить, как объектом пользуются. То, что было описано в других модулях ( в разделе TYPE
интерфейсной части модуля, и реализовано в разделе реализаций) - все было фантомом (объекты и есть
объекты!). Они не способны работать, так как их нет в памяти компьютера. Для того, чтобы занести, нужно создать
экземпляр объекта с помощью таких действий:
Рассылку поддерживает сайт www.turbopascal.tk При перепечатке ссылка на сайт обязательна |
|
(с)Все права защищены По всем интересующим вопросам прошу писать на электронный адрес |