— Romtek 2009/10/30 21:38
Коллекции - это одно из многочисленных применений ООП в программах. Предлагаю вашему вниманию базу данных на основе коллекций. В ней мы рассмотрим такие операции над коллекцией, как добавление, редактирование, просмотр, поиск в коллекции, сохранения и загрузки коллекции.
Почему я решил показать именно на примере коллекций? Да потому, что я вижу их как очень удобную структуру для хранения самой разной информации.
Что можно сделать с коллекциями:
Всё это позволяет механизм наследования. Таким образом, наследовав потомка с новыми характеристиками (допустим, имеющий собственный способ упорядочивания объектов в коллекции), можно добиться новой функциональности программы.
Возьмём для примера такую структуру:
type PRecord = ^TRecord; TRecord = record Name: String[12]; Mark: Integer; end;
Для того, чтобы работать с базой данных все знают, что нам нужен массив, хранящий эти записи:
type TDataArr = array [1..N] of TRecord; DB: file of TDataArr; { описание файловой переменной для работы с файлом }
Для коллекции же необходимо создать объект, в котором будет находиться переменная (в данном случае TransferRecord) типа TRecord (запись):
PStudentInfo=^TStudentInfo; TStudentInfo=object(TObject) TransferRecord: TRecord; constructor Load(var S: TStream); { необходимо перекрывать для операции сохранения в поток (см. ниже) } procedure Store(var S: TStream); { необходимо перекрывать для операции чтения с потока (см. ниже) } Procedure Print; end;
Метод Store нужен для сохранения объекта в поток, а конструктор Load - для его загрузки из потока.
Метод Print выводит на экран данные о студенте из TransferRecord. Это, собственно, всё, что нам
требуется от объекта TStudentInfo.
Остальные операции будут проводиться с коллекцией.
PStudentsColl=^TStudentsColl; TStudentsColl=object(TSortedCollection) function Compare(Key1, Key2: Pointer): Integer; virtual; { перекрываем метод сравнения } end;
TSortedCollection является потомком обычной коллекции TCollection, только у неё имеется возможность сортировки объектов. Т.к. функция сравнения Compare (с её помощью сортируется коллекция) является абстрактной, её надо перекрывать, что мы и сделали:
function TStudentsColl.Compare(Key1, Key2: Pointer): Integer; begin Compare := StrICmp ( PStudentInfo(Key1)^.TransferRecord.Name, PStudentInfo(Key2)^.TransferRecord.Name ); end;
Здесь сравнение ведётся по имени студентов с помощью функции сравнения строк StrICmp (файл sortfunc.inc), без учёта регистра букв. Можно сортировать по любому типу поля любыми видами сравнений.
В разделе инициализации модуля следует зарегистрировать типы RStudentsColl и RStudentInfo, чтобы не возникало ошибок при работе с потоками:
RegisterType (RStudentsColl); RegisterType (RStudentInfo);
At - указатель на объект в коллекции
Insert - добавление в коллекцию
AtDelete - удаление объекта из коллекции
FirstThat - итератор для поиска по заданному условию.
Сам код программы я старался прокоментировать как можно более подробно:
Unit StudObj; Interface Uses Objects; type PRecord = ^TRecord; TRecord = record Name: String[12]; Mark: Integer; end; PStudentInfo=^TStudentInfo; TStudentInfo=object(TObject) TransferRecord: TRecord; constructor Load(var S: TStream); procedure Store(var S: TStream); Procedure Print; Virtual; end; PStudentsColl=^TStudentsColl; TStudentsColl=object(TSortedCollection) function Compare(Key1, Key2: Pointer): Integer; virtual; procedure Error(Code, Info: Integer); virtual; end; const RStudentInfo: TStreamRec = ( ObjType: 55001; VmtLink: Ofs(TypeOf(TStudentInfo)^); Load: @TStudentInfo.Load; Store: @TStudentInfo.Store ); RStudentsColl: TStreamRec = ( ObjType: 55002; VmtLink: Ofs(TypeOf(TStudentsColl)^); Load: @TStudentsColl.Load; Store: @TStudentsColl.Store); var PC: PStudentsColl; Implementation Uses Drivers; type TLinkRecord = record PName: PString; PMark: longint; end; constructor TStudentInfo.Load(var S: TStream); begin Inherited Init; S.Read(TransferRecord, SizeOf(TransferRecord)); end; procedure TStudentInfo.Store(var S: TStream); begin S.Write(TransferRecord, SizeOf(TransferRecord)); end; Procedure TStudentInfo.Print; var str: string; LinkRecord: TLinkRecord; Begin with LinkRecord do begin PName := @TransferRecord.Name; PMark := TransferRecord.Mark; end; FormatStr (str,' %-24s%-4d', LinkRecord); writeln (str); End; procedure TStudentsColl.Error (Code, Info: Integer); begin write ('Error: '); case Code of coIndexError: writeln ('Index out of range.'); coOverflow : writeln ('Collection overflow.'); end; end; {$i sortfunc.inc} function TStudentsColl.Compare (Key1, Key2: Pointer): Integer; begin Compare := StrICmp ( PStudentInfo(Key1)^.TransferRecord.Name, PStudentInfo(Key2)^.TransferRecord.Name ); end; begin RegisterType (RStudentsColl); RegisterType (RStudentInfo); end.
{ Keywords: OOP, Collection, Database Программа является базой данных на основе коллекций. Используются операции добавления, редактирования, удаления, просмотра, поиска в коллекции, сохранения и загрузки коллекции. Примечание: программу несложно изменить для использования интерфейса Turbo Vision. Romtek (c) May 2005 } uses crt, objects, drivers, strings, StudObj; const db_file = 'stud_db.dat'; maxitems = 9; OptionsList: array [1..maxitems] of string[30] = ( 'Load collection from file', 'Save collection to file', 'View collection', 'Add record', 'Edit record', 'Delete record', 'Search record in collection', 'Best students', 'Quit from program' ); var Created, Quit: boolean; option: integer; procedure AbortMsg (msg: string); { Вывести сообщение об ошибке и выйти из программы } begin writeln (msg); halt; end; procedure LoadCollection; { Загрузка сохраненной ранее коллекции из бинарного файла коллекции } var FS: TBufStream; begin FS.Init (db_file, stOpen,1024); if FS.Status <> stOK then AbortMsg ('File not found.'); { Файл не найден } PC := PStudentsColl (FS.Get); { Вставить коллекцию из файла } FS.Done; if FS.Status <> stOK then AbortMsg ('Error of loading collection.'); { Произошла ошибка чтения коллекции. Подробности искать в справке по 'stXXX' } writeln; writeln ('Collection is loaded successfully.'); { Сообщение об успешно загруженной коллекции } readln; end; procedure SaveCollection; { Сохранить коллекцию в файл } var FS: TBufStream; begin FS.Init (db_file, stCreate,1024); { Открываем файл коллекции на создание } FS.Put (PC); { Вставляем коллекцию в файл } FS.Done; { Закрываем работу с потоком } if FS.Status <> stOK then AbortMsg ('Error while saving collection!'); { Произошла ошибка при сохранении коллекции } writeln; writeln ('Collection is stored successfully.'); { Сообщение об успешно сохраненной коллекции } readln; end; procedure ViewCollection (C: PCollection); procedure CallPrint (P : PStudentInfo); far; { Обязательно должна присутствовать директива FAR ! Кроме того, процедура должна быть вложенной, иначе возникнут проблемы. } begin P^.Print; end; begin { Print } Writeln; Writeln; Writeln ('Student list:'); { помощью итератора ForEach выводим информацию о каждом студенте } C^.ForEach (@CallPrint); readln; end; procedure PrintMatches (C: PCollection; _Mark: integer); { Подсчёт и вывод студентов, имеющих оценку свыше указанной _Mark } var count: word; procedure Match (P : PStudentInfo); far; begin if PStudentInfo(P)^.TransferRecord.Mark >= _Mark then begin P^.Print; inc (count); end; end; begin { Print } count:=0; Writeln (#13#10'Students who have mark above ', _Mark,': '); C^.ForEach (@Match); writeln; Writeln ('Total: ', count); readln; end; procedure BestStudents; begin PrintMatches (PC, 80); { Искать студентов, имеющих балл свыше 80 } end; procedure SearchRecord; var P: PStudentInfo; i: integer; who: string; function Matches (Item: Pointer): Boolean; far; begin Matches := PStudentInfo (Item)^.TransferRecord.Name = Who; end; begin write ('Look for student (name): '); readln (who); { Используйте итератор FirstThat, чтобы найти первую "карточку", удовлетворяющую условию } P := PC^.FirstThat (@Matches); i := PC^.IndexOf (P); if i >= 0 then begin writeln ('Student was found:'); PStudentInfo(PC^.At(i))^.Print; end else writeln ('"', Who ,'" was not found.'); readln; end; procedure AddRecord; { Добавление "карточки" в коллекцию } var SI: PStudentInfo; Rec: TRecord; begin writeln('Enter some info about a student: '); SI := New (PStudentInfo, Init); { Создаем "карточку" студента } with SI^.TransferRecord do begin write ('Name: ' : 8); readln (Name); { Вводим новые фамилию } write ('Mark: ' : 8); readln (Mark); { и оценку } end; PC^.Insert (SI); { Вставляем данные о студенте в коллекцию } writeln; writeln ('Record stored successfully.'); { Сообщение об успешно добавленной "карточке" } readln; end; procedure EditRecord; { Редактировать заданную "карточку" студента } var SI: PStudentInfo; Rec: TRecord; num: integer; begin { Прежде чем заменять данные в "карточке" студента, надо ввести его номер в коллекции. Поэтому для начала нужно просмотреть список выбором 'View collection' } write ('Enter number of student in list: '); readln (num); SI := PC^.At (Pred (num)); { Присваиваем указатель на "карточку" студента под номером num-1. Не забывайте, что нумерация в коллекциях идет с нуля! } with SI^.TransferRecord do begin SI^.Print; { Показываем текущие данные из "карточки" студента } writeln; write ('Enter new name: '); readln (Name); { Вводим новые фамилию } write ('Enter new mark: '); readln (Mark); { и оценку } end; writeln; writeln ('Record stored successfully.'); { Сообщение об успешно измененной "карточке" } readln; end; procedure DeleteRecord; { Удаление "карточки" из коллекции } var SI: PStudentInfo; Rec: TRecord; num: integer; begin { Прежде чем удалять данные в "карточке" студента, надо ввести его номер в коллекции. Поэтому для начала нужно просмотреть список выбором 'View collection' } write ('Enter number of student in list: '); readln (num); PC^.AtDelete (Pred (num)); writeln; writeln ('Record deleted successfully.'); { Сообщение об успешно измененной "карточки" } readln; end; procedure ShowMenu; { Построение меню и выбор операции } var i: integer; begin clrscr; writeln ('-= Menu =-': 46); for i := 1 to maxitems do begin writeln; writeln (i: 26, '. ', OptionsList[i]); { Вывод элементов меню } end; writeln; writeln; write ('Enter option: ': 43); readln (option); end; BEGIN Created := true; PC:=New(PStudentsColl,Init(50,10)); { Создание коллекции в памяти из 50 объектов. Цифра 10 говорит о том, что если попытаемся вставить в коллекцию больше чем 50 объектов, то станем увеличивать начальный размер (50) на 10, пока не хватит места для всех объектов. } if PC = Nil then { Увы, не хватило памяти... Выходим. } exit; repeat ShowMenu; { Выбор опции } case option of 1: if PC^.Count = 0 then { Коллекция пуста } LoadCollection; 2: SaveCollection; 3: ViewCollection (PC); 4: AddRecord; 5: EditRecord; 6: DeleteRecord; 7: SearchRecord; 8: BestStudents; 9: Quit := true; { Получили команду выйти из программы } end; until Quit; { Освободить память от коллекции } Dispose(PC, Done); END.
Полная версия находится в файле.
Программа основана на программе выше, только добавлен интерсфейс Turbo Vision (как среда BP7). Аналогично, рассмотрены и реализованы следующие операции: добавление, редактирование, удаление, просмотр, поиск в коллекции, сохранения и загрузки коллекции в файл.